private async Task <MevBlockProducer.MevBlockProducerInfo> CreateProducer(
            IConsensusPlugin consensusPlugin,
            int bundleLimit = 0,
            ITxSource?additionalTxSource = null)
        {
            bool BundleLimitTriggerCondition(BlockProductionEventArgs e)
            {
                // TODO: why we are checking parent and not the currently produced block...?
                BlockHeader?parent = _nethermindApi.BlockTree !.GetProducedBlockParent(e.ParentHeader);

                if (parent is not null)
                {
                    IEnumerable <MevBundle> bundles = BundlePool.GetBundles(parent, _nethermindApi.Timestamper);
                    return(bundles.Count() >= bundleLimit);
                }

                return(false);
            }

            IManualBlockProductionTrigger manualTrigger = new BuildBlocksWhenRequested();
            IBlockProductionTrigger       trigger       = manualTrigger;

            if (bundleLimit != 0)
            {
                trigger = new TriggerWithCondition(manualTrigger, BundleLimitTriggerCondition);
            }

            IBlockProducer producer = await consensusPlugin.InitBlockProducer(trigger, additionalTxSource);

            return(new MevBlockProducer.MevBlockProducerInfo(producer, manualTrigger, new BeneficiaryTracer()));
        }
        public Task <IBlockProducer> InitBlockProducer(IConsensusPlugin consensusPlugin)
        {
            if (!Enabled)
            {
                throw new InvalidOperationException("Account Abstraction plugin is disabled");
            }

            UInt256 minerBalance = _nethermindApi.StateProvider !.GetBalance(_nethermindApi.EngineSigner !.Address);

            if (minerBalance < 1.Ether())
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn(
                        $"Account Abstraction Plugin: Miner ({_nethermindApi.EngineSigner!.Address}) Ether balance low - {minerBalance / 1.Ether()} Ether < 1 Ether. Increasing balance is recommended");
                }
                else
                {
                    if (_logger.IsInfo)
                    {
                        _logger.Info(
                            $"Account Abstraction Plugin: Miner ({_nethermindApi.EngineSigner!.Address}) Ether balance adequate - {minerBalance / 1.Ether()} Ether");
                    }
                }
            }

            IManualBlockProductionTrigger trigger = new BuildBlocksWhenRequested();

            return(consensusPlugin.InitBlockProducer(trigger, UserOperationTxSource));
        }
        public Task <IBlockProducer> InitBlockProducer(IConsensusPlugin consensusPlugin)
        {
            if (!Enabled)
            {
                throw new InvalidOperationException("Account Abstraction plugin is disabled");
            }

            // init all relevant objects if not already initted
            foreach (Address entryPoint in _entryPointContractAddresses)
            {
                UserOperationPool(entryPoint);
                UserOperationSimulator(entryPoint);
                UserOperationTxBuilder(entryPoint);
            }

            _nethermindApi.BlockProducerEnvFactory.TransactionsExecutorFactory =
                new AABlockProducerTransactionsExecutorFactory(
                    _nethermindApi.SpecProvider !,
                    _nethermindApi.LogManager !,
                    _nethermindApi.EngineSigner !,
                    _entryPointContractAddresses.ToArray());

            UInt256 minerBalance = _nethermindApi.ChainHeadStateProvider !.GetBalance(_nethermindApi.EngineSigner !.Address);

            if (minerBalance < 1.Ether())
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn(
                        $"Account Abstraction Plugin: Miner ({_nethermindApi.EngineSigner!.Address}) Ether balance low - {minerBalance / 1.Ether()} Ether < 1 Ether. Increasing balance is recommended");
                }
                else
                {
                    if (_logger.IsInfo)
                    {
                        _logger.Info(
                            $"Account Abstraction Plugin: Miner ({_nethermindApi.EngineSigner!.Address}) Ether balance adequate - {minerBalance / 1.Ether()} Ether");
                    }
                }
            }

            IManualBlockProductionTrigger trigger = new BuildBlocksWhenRequested();

            return(consensusPlugin.InitBlockProducer(trigger, UserOperationTxSource));
        }
        public async Task Can_initialize_block_producer()
        {
            // Setup
            MevPlugin     plugin  = new();
            NethermindApi context = Runner.Test.Ethereum.Build.ContextWithMocks();

            await plugin.Init(context);

            plugin.Enabled.Returns(true);
            await plugin.InitRpcModules();

            IConsensusPlugin consensusPlugin = Substitute.For <IConsensusPlugin>();

            consensusPlugin.InitBlockProducer().Returns(Substitute.For <IBlockProducer>());

            Task <IBlockProducer> blockProducer = plugin.InitBlockProducer(consensusPlugin);

            blockProducer.Result.Should().BeOfType(typeof(MevBlockProducer));
        }
        public async Task <IBlockProducer> InitBlockProducer(IConsensusPlugin consensusPlugin)
        {
            if (!Enabled)
            {
                throw new InvalidOperationException("Plugin is disabled");
            }

            _nethermindApi.BlockProducerEnvFactory.TransactionsExecutorFactory = new MevBlockProducerTransactionsExecutorFactory(_nethermindApi.SpecProvider !, _nethermindApi.LogManager);

            int megabundleProducerCount = _mevConfig.GetTrustedRelayAddresses().Any() ? 1 : 0;
            List <MevBlockProducer.MevBlockProducerInfo> blockProducers =
                new(_mevConfig.MaxMergedBundles + megabundleProducerCount + 1);

            // Add non-mev block
            MevBlockProducer.MevBlockProducerInfo standardProducer = await CreateProducer(consensusPlugin);

            blockProducers.Add(standardProducer);

            // Try blocks with all bundle numbers <= MaxMergedBundles
            for (int bundleLimit = 1; bundleLimit <= _mevConfig.MaxMergedBundles; bundleLimit++)
            {
                BundleSelector bundleSelector = new(BundlePool, bundleLimit);
                MevBlockProducer.MevBlockProducerInfo bundleProducer = await CreateProducer(consensusPlugin, bundleLimit, new BundleTxSource(bundleSelector, _nethermindApi.Timestamper));

                blockProducers.Add(bundleProducer);
            }

            if (megabundleProducerCount > 0)
            {
                MegabundleSelector megabundleSelector = new(BundlePool);
                MevBlockProducer.MevBlockProducerInfo bundleProducer = await CreateProducer(consensusPlugin, 0, new BundleTxSource(megabundleSelector, _nethermindApi.Timestamper));

                blockProducers.Add(bundleProducer);
            }

            return(new MevBlockProducer(consensusPlugin.DefaultBlockProductionTrigger, _nethermindApi.LogManager, blockProducers.ToArray()));
        }
        public async Task <IBlockProducer> InitBlockProducer(IConsensusPlugin consensusPlugin)
        {
            if (MergeEnabled)
            {
                if (_api.EngineSigner == null)
                {
                    throw new ArgumentNullException(nameof(_api.EngineSigner));
                }
                if (_api.ChainSpec == null)
                {
                    throw new ArgumentNullException(nameof(_api.ChainSpec));
                }
                if (_api.BlockTree == null)
                {
                    throw new ArgumentNullException(nameof(_api.BlockTree));
                }
                if (_api.BlockProcessingQueue == null)
                {
                    throw new ArgumentNullException(nameof(_api.BlockProcessingQueue));
                }
                if (_api.SpecProvider == null)
                {
                    throw new ArgumentNullException(nameof(_api.SpecProvider));
                }
                if (_api.BlockValidator == null)
                {
                    throw new ArgumentNullException(nameof(_api.BlockValidator));
                }
                if (_api.RewardCalculatorSource == null)
                {
                    throw new ArgumentNullException(nameof(_api.RewardCalculatorSource));
                }
                if (_api.ReceiptStorage == null)
                {
                    throw new ArgumentNullException(nameof(_api.ReceiptStorage));
                }
                if (_api.TxPool == null)
                {
                    throw new ArgumentNullException(nameof(_api.TxPool));
                }
                if (_api.DbProvider == null)
                {
                    throw new ArgumentNullException(nameof(_api.DbProvider));
                }
                if (_api.ReadOnlyTrieStore == null)
                {
                    throw new ArgumentNullException(nameof(_api.ReadOnlyTrieStore));
                }
                if (_api.BlockchainProcessor == null)
                {
                    throw new ArgumentNullException(nameof(_api.BlockchainProcessor));
                }
                if (_api.HeaderValidator == null)
                {
                    throw new ArgumentNullException(nameof(_api.HeaderValidator));
                }
                if (_mergeBlockProductionPolicy == null)
                {
                    throw new ArgumentNullException(nameof(_mergeBlockProductionPolicy));
                }
                if (_api.SealValidator == null)
                {
                    throw new ArgumentNullException(nameof(_api.SealValidator));
                }

                if (_logger.IsInfo)
                {
                    _logger.Info("Starting Merge block producer & sealer");
                }

                IBlockProducer?blockProducer = _mergeBlockProductionPolicy.ShouldInitPreMergeBlockProduction()
                    ? await consensusPlugin.InitBlockProducer()
                    : null;

                _miningConfig = _api.Config <IMiningConfig>();
                _manualTimestamper ??= new ManualTimestamper();
                _blockProductionTrigger = new BuildBlocksWhenRequested();
                BlockProducerEnv blockProducerEnv = _api.BlockProducerEnvFactory.Create();

                _api.SealEngine = new MergeSealEngine(_api.SealEngine, _poSSwitcher, _api.SealValidator, _api.LogManager);
                _api.Sealer     = _api.SealEngine;
                PostMergeBlockProducerFactory blockProducerFactory = new(_api.SpecProvider, _api.SealEngine, _manualTimestamper, _miningConfig, _api.LogManager);
                _postMergeBlockProducer = blockProducerFactory.Create(
                    blockProducerEnv,
                    _blockProductionTrigger,
                    CreateTxSource(blockProducerEnv.ReadOnlyStateProvider)
                    );

                _api.BlockProducer = new MergeBlockProducer(blockProducer, _postMergeBlockProducer, _poSSwitcher);
            }

            return(_api.BlockProducer !);
        }