public async Task forkchoiceUpdatedV1_should_ignore_gas_limit([Values(false, true)] bool relay)
    {
        MergeConfig mergeConfig = new() { Enabled = true, SecondsPerSlot = 1, TerminalTotalDifficulty = "0" };

        using MergeTestBlockchain chain = await CreateBlockChain(mergeConfig);

        IBlockImprovementContextFactory improvementContextFactory;

        if (relay)
        {
            IBoostRelay boostRelay = Substitute.For <IBoostRelay>();
            boostRelay.GetPayloadAttributes(Arg.Any <PayloadAttributes>(), Arg.Any <CancellationToken>())
            .Returns(c => c.Arg <PayloadAttributes>());

            improvementContextFactory = new BoostBlockImprovementContextFactory(chain.BlockProductionTrigger, TimeSpan.FromSeconds(5), boostRelay, chain.StateReader);
        }
        else
        {
            improvementContextFactory = new BlockImprovementContextFactory(chain.BlockProductionTrigger, TimeSpan.FromSeconds(5));
        }

        TimeSpan timePerSlot = TimeSpan.FromSeconds(10);

        chain.PayloadPreparationService = new PayloadPreparationService(
            chain.PostMergeBlockProducer !,
            improvementContextFactory,
            TimerFactory.Default,
            chain.LogManager,
            timePerSlot);

        IEngineRpcModule rpc          = CreateEngineModule(chain);
        Keccak           startingHead = chain.BlockTree.HeadHash;
        UInt256          timestamp    = Timestamper.UnixTime.Seconds;
        Keccak           random       = Keccak.Zero;
        Address          feeRecipient = Address.Zero;

        string payloadId = rpc.engine_forkchoiceUpdatedV1(new ForkchoiceStateV1(startingHead, Keccak.Zero, startingHead),
                                                          new PayloadAttributes {
            Timestamp = timestamp, SuggestedFeeRecipient = feeRecipient, PrevRandao = random, GasLimit = 10_000_000L
        }).Result.Data
                           .PayloadId !;

        ResultWrapper <ExecutionPayloadV1?> response = await rpc.engine_getPayloadV1(Bytes.FromHexString(payloadId));

        ExecutionPayloadV1 executionPayloadV1 = response.Data !;

        executionPayloadV1.GasLimit.Should().Be(4_000_000L);
    }
    private async Task <Block?> StartImprovingBlock(
        IManualBlockProductionTrigger blockProductionTrigger,
        BlockHeader parentHeader,
        PayloadAttributes payloadAttributes,
        CancellationToken cancellationToken)
    {
        payloadAttributes = await _boostRelay.GetPayloadAttributes(payloadAttributes, cancellationToken);

        UInt256 balanceBefore = _stateReader.GetAccount(parentHeader.StateRoot !, payloadAttributes.SuggestedFeeRecipient)?.Balance ?? UInt256.Zero;
        Block?  block         = await blockProductionTrigger.BuildBlock(parentHeader, cancellationToken, NullBlockTracer.Instance, payloadAttributes);

        if (block is not null)
        {
            CurrentBestBlock = block;
            UInt256 balanceAfter = _stateReader.GetAccount(block.StateRoot !, payloadAttributes.SuggestedFeeRecipient)?.Balance ?? UInt256.Zero;
            await _boostRelay.SendPayload(new BoostExecutionPayloadV1 { Block = new ExecutionPayloadV1(block), Profit = balanceAfter - balanceBefore }, cancellationToken);
        }

        return(CurrentBestBlock);
    }
    public async Task forkchoiceUpdatedV1_should_communicate_with_boost_relay()
    {
        MergeConfig mergeConfig = new() { Enabled = true, SecondsPerSlot = 1, TerminalTotalDifficulty = "0" };

        using MergeTestBlockchain chain = await CreateBlockChain(mergeConfig);

        IBoostRelay boostRelay = Substitute.For <IBoostRelay>();

        boostRelay.GetPayloadAttributes(Arg.Any <PayloadAttributes>(), Arg.Any <CancellationToken>())
        .Returns(c =>
        {
            PayloadAttributes payloadAttributes     = c.Arg <PayloadAttributes>();
            payloadAttributes.SuggestedFeeRecipient = TestItem.AddressA;
            payloadAttributes.PrevRandao            = TestItem.KeccakA;
            payloadAttributes.Timestamp            += 1;
            payloadAttributes.GasLimit = 10_000_000L;
            return(payloadAttributes);
        });

        BoostBlockImprovementContextFactory improvementContextFactory = new(chain.BlockProductionTrigger, TimeSpan.FromSeconds(5), boostRelay, chain.StateReader);
        TimeSpan timePerSlot = TimeSpan.FromSeconds(10);

        chain.PayloadPreparationService = new PayloadPreparationService(
            chain.PostMergeBlockProducer !,
            improvementContextFactory,
            TimerFactory.Default,
            chain.LogManager,
            timePerSlot);

        IEngineRpcModule rpc          = CreateEngineModule(chain);
        Keccak           startingHead = chain.BlockTree.HeadHash;
        UInt256          timestamp    = Timestamper.UnixTime.Seconds;
        Keccak           random       = Keccak.Zero;
        Address          feeRecipient = Address.Zero;

        BoostExecutionPayloadV1?sentItem = null;

        boostRelay.When(b => b.SendPayload(Arg.Any <BoostExecutionPayloadV1>(), Arg.Any <CancellationToken>()))
        .Do(c => sentItem = c.Arg <BoostExecutionPayloadV1>());

        ManualResetEvent wait = new(false);

        chain.PayloadPreparationService.BlockImproved += (_, _) => wait.Set();

        string payloadId = rpc.engine_forkchoiceUpdatedV1(new ForkchoiceStateV1(startingHead, Keccak.Zero, startingHead),
                                                          new PayloadAttributes {
            Timestamp = timestamp, SuggestedFeeRecipient = feeRecipient, PrevRandao = random
        }).Result.Data
                           .PayloadId !;

        await wait.WaitOneAsync(100, CancellationToken.None);

        ResultWrapper <ExecutionPayloadV1?> response = await rpc.engine_getPayloadV1(Bytes.FromHexString(payloadId));

        ExecutionPayloadV1 executionPayloadV1 = response.Data !;

        executionPayloadV1.FeeRecipient.Should().Be(TestItem.AddressA);
        executionPayloadV1.PrevRandao.Should().Be(TestItem.KeccakA);
        executionPayloadV1.GasLimit.Should().Be(10_000_000L);
        executionPayloadV1.Should().BeEquivalentTo(sentItem !.Block);
        sentItem.Profit.Should().Be(0);
    }