@@ -85,6 +85,17 @@ namespace pwiz.Skyline.Model.Databinding.Entities
         [Format(NullValue = TextUtil.EXCEL_NA)]
         public int? PointsAcrossPeak { get { return ChromInfo.PointsAcrossPeak; } }

+        [Format(Formats.RETENTION_TIME, NullValue = TextUtil.EXCEL_NA)]
+        public double? AverageCycleTime
+        {
+            get
+            {
+                return StartTime.HasValue && EndTime.HasValue && PointsAcrossPeak.HasValue
+                    ? (EndTime.Value - StartTime.Value) * 60 / PointsAcrossPeak.Value
+                    : (double?) null;
+            }
+        }
+
         public bool Coeluting { get { return !ChromInfo.IsForcedIntegration; } }

         [Format(Formats.RETENTION_TIME, NullValue = TextUtil.EXCEL_NA)]

/*
 * Original author: Nicholas Shulman <nicksh .at. u.washington.edu>,
 *                  MacCoss Lab, Department of Genome Sciences, UW
 *
 * Copyright 2012 University of Washington - Seattle, WA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.ComponentModel;
using pwiz.Common.DataBinding.Attributes;
using pwiz.Skyline.Model.DocSettings;
using pwiz.Skyline.Model.ElementLocators;
using pwiz.Skyline.Model.Hibernate;
using pwiz.Skyline.Model.Results;
using pwiz.Skyline.Util.Extensions;

namespace pwiz.Skyline.Model.Databinding.Entities
{
    [InvariantDisplayName(nameof(TransitionResult))]
    [AnnotationTarget(AnnotationDef.AnnotationTarget.transition_result)]
    public class TransitionResult : Result
    {
        private readonly CachedValue<TransitionChromInfo> _chromInfo;
        private readonly CachedValue<Chromatogram> _chromatogram;
        public TransitionResult(Transition transition, ResultFile resultFile) : base(transition, resultFile)
        {
            _chromInfo = CachedValue.Create(DataSchema, () => GetResultFile().FindChromInfo(transition.DocNode.Results));
            _chromatogram = CachedValue.Create(DataSchema,
                () => new Chromatogram(new ChromatogramGroup(PrecursorResult), Transition));
        }

        [Browsable(false)]
        public TransitionChromInfo ChromInfo { get { return _chromInfo.Value; } }

        public void ChangeChromInfo(EditDescription editDescription, Func<TransitionChromInfo, TransitionChromInfo> newChromInfo)
        {
            Transition.ChangeDocNode(editDescription, docNode=>docNode.ChangeResults(GetResultFile().ChangeChromInfo(docNode.Results, newChromInfo)));
        }
        [HideWhen(AncestorOfType = typeof(Transition))]
        public Transition Transition { get { return (Transition)SkylineDocNode; } }
        [Format(Formats.RETENTION_TIME, NullValue = TextUtil.EXCEL_NA)]
        public double? RetentionTime { get { return ChromInfo.IsEmpty ? (double?) null : ChromInfo.RetentionTime; } }
        [Format(Formats.RETENTION_TIME, NullValue = TextUtil.EXCEL_NA)]
        public double? Fwhm { get { return ChromInfo.IsEmpty ? (double?) null : ChromInfo.Fwhm; } }
        public bool FwhmDegenerate { get { return ChromInfo.IsFwhmDegenerate; } }
        [Format(Formats.RETENTION_TIME, NullValue = TextUtil.EXCEL_NA)]
        public double? StartTime { get { return ChromInfo.IsEmpty ? (double?)null : ChromInfo.StartRetentionTime; } }
        [Format(Formats.RETENTION_TIME, NullValue = TextUtil.EXCEL_NA)]
        public double? EndTime { get { return ChromInfo.IsEmpty ? (double?) null : ChromInfo.EndRetentionTime; } }
        [Format(Formats.PEAK_AREA, NullValue = TextUtil.EXCEL_NA)]
        public double? Area { get { return ChromInfo.IsEmpty ? (double?) null : ChromInfo.Area; } }
        [Format(Formats.PEAK_AREA, NullValue = TextUtil.EXCEL_NA)]
        public double? Background { get { return ChromInfo.IsEmpty ? (double?)null : ChromInfo.BackgroundArea; } }
        [Format(Formats.STANDARD_RATIO, NullValue = TextUtil.EXCEL_NA)]
        public double? AreaRatio { get { return ChromInfo.Ratio; } }
        [Format(Formats.PEAK_AREA_NORMALIZED, NullValue = TextUtil.EXCEL_NA)]
        public double? AreaNormalized
        {
            get { return Area / GetResultFile().GetTotalArea(Transition.Precursor.IsotopeLabelType); }
        }
        [Format(Formats.PEAK_AREA, NullValue = TextUtil.EXCEL_NA)]
        public double? Height { get { return ChromInfo.IsEmpty ? (double?) null : ChromInfo.Height; } }
        [Format(Formats.MASS_ERROR, NullValue = TextUtil.EXCEL_NA)]
        public double? MassErrorPPM { get { return ChromInfo.MassError; } }
        public bool? Truncated { get { return ChromInfo.IsTruncated; } }
        [Format(NullValue = TextUtil.EXCEL_NA)]
        public int? PeakRank { get { return ChromInfo.IsEmpty ? (int?)null : ChromInfo.Rank; } }
        [Format(NullValue = TextUtil.EXCEL_NA)]
        public int? PeakRankByLevel { get { return ChromInfo.IsEmpty ? (int?)null : ChromInfo.RankByLevel; } }
        public UserSet UserSetPeak { get { return ChromInfo.UserSet; } }
        [Format(NullValue = TextUtil.EXCEL_NA)]
        public int OptStep { get { return ChromInfo.OptimizationStep; } }
        [Format(NullValue = TextUtil.EXCEL_NA)]
        public int? PointsAcrossPeak { get { return ChromInfo.PointsAcrossPeak; } }

        public bool Coeluting { get { return !ChromInfo.IsForcedIntegration; } }

        [Format(Formats.RETENTION_TIME, NullValue = TextUtil.EXCEL_NA)]
        public double? IonMobilityFragment
        {
            get
            {
                return IonMobilityFilter.IsNullOrEmpty(ChromInfo.IonMobility) ? null
                    : ChromInfo.IonMobility.IonMobilityAndCCS.GetHighEnergyIonMobility();
            }
        }

        public Chromatogram Chromatogram
        {
            get { return _chromatogram.Value; }
        }

        [InvariantDisplayName("TransitionReplicateNote")]
        [Importable]
        public string Note
        {
            get { return ChromInfo.Annotations.Note; }
            set
            {
                ChangeChromInfo(EditColumnDescription(nameof(Note), value),
                    chromInfo=>chromInfo.ChangeAnnotations(chromInfo.Annotations.ChangeNote(value)));
            }
        }

        public override void SetAnnotation(AnnotationDef annotationDef, object value)
        {
            ChangeChromInfo(EditDescription.SetAnnotation(annotationDef, value),
                chromInfo=>chromInfo.ChangeAnnotations(chromInfo.Annotations.ChangeAnnotation(annotationDef, value)));
        }

        public override object GetAnnotation(AnnotationDef annotationDef)
        {
            return DataSchema.AnnotationCalculator.GetAnnotation(annotationDef, this, ChromInfo.Annotations);
        }

        [HideWhen(AncestorOfType = typeof(SkylineDocument))]
        public PrecursorResult PrecursorResult
        {
            get
            {
                return new PrecursorResult(Transition.Precursor, GetResultFile());
            }
        }
        public override string ToString()
        {
            return string.Format(@"{0:0}", ChromInfo.Area);
        }

        [InvariantDisplayName("TransitionResultLocator")]
        public string Locator
        {
            get { return GetLocator(); }
        }

        public override ElementRef GetElementRef()
        {
            return TransitionResultRef.PROTOTYPE.ChangeChromInfo(GetResultFile().Replicate.ChromatogramSet, ChromInfo)
                .ChangeParent(Transition.GetElementRef());
        }

        public override bool IsEmpty()
        {
            return ChromInfo.IsEmpty;
        }
    }
}

You can simplify this by doing: return (EndTime - StartTime) * 60 / PointsAcrossPeak;


@@ -34,14 +34,10 @@ namespace OpenTelemetry.Exporter.Jaeger
         private readonly IJaegerUdpBatcher jaegerAgentUdpBatcher;
         private bool disposedValue = false; // To detect redundant dispose calls

-        public JaegerTraceExporter(JaegerExporterOptions options)
+        public JaegerTraceExporter(JaegerExporterOptions options, IJaegerUdpBatcher jaegerAgentUdpBatcher)
         {
             this.ValidateOptions(options);
             this.InitializeOptions(options);
-        }
-
-        public JaegerTraceExporter(IJaegerUdpBatcher jaegerAgentUdpBatcher)
-        {
             this.jaegerAgentUdpBatcher = jaegerAgentUdpBatcher;
         }

// <copyright file="JaegerTraceExporter.cs" company="OpenTelemetry Authors">
// Copyright 2018, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

namespace OpenTelemetry.Exporter.Jaeger
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using OpenTelemetry.Exporter.Jaeger.Implementation;
    using OpenTelemetry.Trace;
    using OpenTelemetry.Trace.Export;

    public class JaegerTraceExporter : SpanExporter, IDisposable
    {
        public const string DefaultAgentUdpHost = "localhost";
        public const int DefaultAgentUdpCompactPort = 6831;
        public const int DefaultMaxPacketSize = 65000;

        private readonly IJaegerUdpBatcher jaegerAgentUdpBatcher;
        private bool disposedValue = false; // To detect redundant dispose calls

        public JaegerTraceExporter(JaegerExporterOptions options)
        {
            this.ValidateOptions(options);
            this.InitializeOptions(options);
        }

        public JaegerTraceExporter(IJaegerUdpBatcher jaegerAgentUdpBatcher)
        {
            this.jaegerAgentUdpBatcher = jaegerAgentUdpBatcher;
        }

        public override async Task<ExportResult> ExportAsync(IEnumerable<Span> otelSpanList, CancellationToken cancellationToken)
        {
            var jaegerspans = otelSpanList.Select(sdl => sdl.ToJaegerSpan());

            foreach (var s in jaegerspans)
            {
                // avoid cancelling here: this is no return point: if we reached this point
                // and cancellation is requested, it's better if we try to finish sending spans rather than drop it
                await this.jaegerAgentUdpBatcher.AppendAsync(s, CancellationToken.None);
            }

            // TODO jaeger status to ExportResult
            return ExportResult.Success;
        }

        public override Task ShutdownAsync(CancellationToken cancellationToken)
        {
            return this.jaegerAgentUdpBatcher.FlushAsync(cancellationToken);
        }

        public void Dispose()
        {
            // Do not change this code. Put cleanup code in Dispose(bool disposing).
            this.Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposedValue)
            {
                if (disposing)
                {
                    this.jaegerAgentUdpBatcher.Dispose();
                }

                this.disposedValue = true;
            }
        }

        private void ValidateOptions(JaegerExporterOptions options)
        {
            if (string.IsNullOrWhiteSpace(options.ServiceName))
            {
                throw new ArgumentException("ServiceName", "Service Name is required.");
            }
        }

        private void InitializeOptions(JaegerExporterOptions options)
        {
            if (string.IsNullOrWhiteSpace(options.AgentHost))
            {
                options.AgentHost = DefaultAgentUdpHost;
            }

            if (!options.AgentPort.HasValue)
            {
                options.AgentPort = DefaultAgentUdpCompactPort;
            }

            if (!options.MaxPacketSize.HasValue)
            {
                options.MaxPacketSize = DefaultMaxPacketSize;
            }
        }
    }
}

Alternatively we can keep the existing constructor and default to use `JaegerUdpBatcher`


@@ -120,10 +120,17 @@ namespace Microsoft.DotNet.Build.CloudTestTasks
                     Log.LogMessage(MessageImportance.Normal, "Received response to check whether Container blobs exist");
                 }
             }
-            await ThreadingTask.WhenAll(Items.Select(item => UploadAsync(ct, item, blobsPresent)));
-
-           Log.LogMessage(MessageImportance.High, "Upload to Azure is complete, a total of {0} items were uploaded.", Items.Length);
+            try
+            {
+                await ThreadingTask.WhenAll(Items.Select(item => UploadAsync(ct, item, blobsPresent)));
+            }
+            catch (Exception)
+            {
+                Log.LogError("Failed to upload to Azure");
+                return false;
+            }

+            Log.LogMessage(MessageImportance.High, "Upload to Azure is complete, a total of {0} items were uploaded.", Items.Length);
             return true;
         }

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Linq;
using System.Net.Http;

using Microsoft.Build.Framework;

using Task = Microsoft.Build.Utilities.Task;
using ThreadingTask = System.Threading.Tasks.Task;

namespace Microsoft.DotNet.Build.CloudTestTasks
{

    public class UploadToAzure : Task, ICancelableTask
    {
        private static readonly CancellationTokenSource TokenSource = new CancellationTokenSource();
        private static readonly CancellationToken CancellationToken = TokenSource.Token;

        /// <summary>
        ///     The Azure account key used when creating the connection string.
        /// </summary>
        [Required]
        public string AccountKey { get; set; }

        /// <summary>
        ///     The Azure account name used when creating the connection string.
        /// </summary>
        [Required]
        public string AccountName { get; set; }

        /// <summary>
        ///     The name of the container to access.  The specified name must be in the correct format, see the
        ///     following page for more info.  https://msdn.microsoft.com/en-us/library/azure/dd135715.aspx
        /// </summary>
        [Required]
        public string ContainerName { get; set; }

        /// <summary>
        ///     An item group of files to upload.  Each item must have metadata RelativeBlobPath
        ///     that specifies the path relative to ContainerName where the item will be uploaded.
        /// </summary>
        [Required]
        public ITaskItem[] Items { get; set; }

        /// <summary>
        ///     Indicates if the destination blob should be overwritten if it already exists.  The default if false.
        /// </summary>
        public bool Overwrite { get; set; } = false;

        public void Cancel()
        {
            TokenSource.Cancel();
        }

        public override bool Execute()
        {
            return ExecuteAsync(CancellationToken).GetAwaiter().GetResult();
        }

        public async Task<bool> ExecuteAsync(CancellationToken ct)
        {
            Log.LogMessage(
                MessageImportance.High,
                "Begin uploading blobs to Azure account {0} in container {1}.",
                AccountName,
                ContainerName);

            if (Items.Length == 0)
            {
                throw new ArgumentException("No items were provided for upload.");
            }

            // first check what blobs are present
            string checkListUrl = string.Format(
                "https://{0}.blob.core.windows.net/{1}?restype=container&comp=list",
                AccountName,
                ContainerName);

            DateTime dt = DateTime.UtcNow;
            HashSet<string> blobsPresent = new HashSet<string>();

            using (HttpClient client = new HttpClient())
            {
                using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, checkListUrl))
                {
                    req.Headers.Add(AzureHelper.DateHeaderString, dt.ToString("R", CultureInfo.InvariantCulture));
                    req.Headers.Add(AzureHelper.VersionHeaderString, AzureHelper.StorageApiVersion);
                    req.Headers.Add(AzureHelper.AuthorizationHeaderString, AzureHelper.AuthorizationHeader(
                        AccountName,
                        AccountKey,
                        "GET",
                        dt,
                        req));

                    Log.LogMessage(MessageImportance.Normal, "Sending request to check whether Container blobs exist");
                    XmlDocument doc;
                    using (HttpResponseMessage response = await client.SendAsync(req, ct))
                    {
                        doc = new XmlDocument();
                        doc.LoadXml(await response.Content.ReadAsStringAsync());
                    }

                    XmlNodeList nodes = doc.DocumentElement.GetElementsByTagName("Blob");

                    foreach (XmlNode node in nodes)
                    {
                        blobsPresent.Add(node["Name"].InnerText);
                    }

                    Log.LogMessage(MessageImportance.Normal, "Received response to check whether Container blobs exist");
                }
            }
            await ThreadingTask.WhenAll(Items.Select(item => UploadAsync(ct, item, blobsPresent)));

           Log.LogMessage(MessageImportance.High, "Upload to Azure is complete, a total of {0} items were uploaded.", Items.Length);

            return true;
        }

        private async ThreadingTask UploadAsync(CancellationToken ct, ITaskItem item, HashSet<string> blobsPresent)
        {
            bool result = true;
            if (ct.IsCancellationRequested)
            {
                Log.LogError("Task UploadToAzure cancelled");
                ct.ThrowIfCancellationRequested();
            }

            string relativeBlobPath = item.GetMetadata("RelativeBlobPath");
            if (string.IsNullOrEmpty(relativeBlobPath))
            {
                Log.LogError(string.Format("Metadata 'RelativeBlobPath' is missing for item '{0}'.", item.ItemSpec));
                result = false;
            }

            if (!File.Exists(item.ItemSpec))
            {
                Log.LogError(string.Format("The file '{0}' does not exist.", item.ItemSpec));
                result = false;
            }

            if (!Overwrite && blobsPresent.Contains(relativeBlobPath))
            {
                Log.LogError(string.Format("The blob '{0}' already exists.", relativeBlobPath));
                result = false;
            }

            if (result)
            {
                Log.LogMessage("Uploading {0} to {1}.", item.ItemSpec, ContainerName);
                UploadClient uploadClient = new UploadClient(Log);
                await
                    uploadClient.UploadBlockBlobAsync(
                        ct,
                        AccountName,
                        AccountKey,
                        ContainerName,
                        item.ItemSpec,
                        relativeBlobPath);
            }
        }
    }
}

You can use LogErrorFromException instead :smile:


@@ -78,8 +78,9 @@ namespace NLog.Internal
         /// </summary>
         /// <param name="exception">The exception to check.</param>
         /// <param name="loggerContext">Target context of the exception.</param>
+        /// <param name="logFactory">For checking <see cref="LogFactory.ThrowExceptions"/> and <see cref="LogFactory.ThrowConfigExceptions"/>. Falls back to <see cref="LogManager"/> </param>
         /// <returns><c>true</c>if the <paramref name="exception"/> must be rethrown, <c>false</c> otherwise.</returns>
-        public static bool MustBeRethrown(this Exception exception, IInternalLoggerContext loggerContext = null)
+        public static bool MustBeRethrown(this Exception exception, IInternalLoggerContext loggerContext = null, LogFactory logFactory = null)
         {
             if (exception.MustBeRethrownImmediately())
             {

//
// Copyright (c) 2004-2020 Jaroslaw Kowalski <jaak@jkowalski.net>, Kim Christensen, Julian Verdurmen
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of Jaroslaw Kowalski nor the names of its
//   contributors may be used to endorse or promote products derived from this
//   software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//

namespace NLog.Internal
{
    using System;
    using System.Threading;
    using NLog.Common;

    /// <summary>
    /// Helper class for dealing with exceptions.
    /// </summary>
    internal static class ExceptionHelper
    {
        private const string LoggedKey = "NLog.ExceptionLoggedToInternalLogger";

        /// <summary>
        /// Mark this exception as logged to the <see cref="InternalLogger"/>.
        /// </summary>
        /// <param name="exception"></param>
        /// <returns></returns>
        public static void MarkAsLoggedToInternalLogger(this Exception exception)
        {
            if (exception != null)
            {
                exception.Data[LoggedKey] = true;
            }
        }

        /// <summary>
        /// Is this exception logged to the <see cref="InternalLogger"/>?
        /// </summary>
        /// <param name="exception"></param>
        /// <returns><c>true</c>if the <paramref name="exception"/> has been logged to the <see cref="InternalLogger"/>.</returns>
        public static bool IsLoggedToInternalLogger(this Exception exception)
        {
            if (exception?.Data?.Count > 0)
            {
                return exception.Data[LoggedKey] as bool? ?? false;
            }
            return false;
        }

        /// <summary>
        /// Determines whether the exception must be rethrown and logs the error to the <see cref="InternalLogger"/> if <see cref="IsLoggedToInternalLogger"/> is <c>false</c>.
        ///
        /// Advised to log first the error to the <see cref="InternalLogger"/> before calling this method.
        /// </summary>
        /// <param name="exception">The exception to check.</param>
        /// <param name="loggerContext">Target context of the exception.</param>
        /// <returns><c>true</c>if the <paramref name="exception"/> must be rethrown, <c>false</c> otherwise.</returns>
        public static bool MustBeRethrown(this Exception exception, IInternalLoggerContext loggerContext = null)
        {
            if (exception.MustBeRethrownImmediately())
            {
                //no further logging, because it can make severe exceptions only worse.
                return true;
            }

            var isConfigError = exception is NLogConfigurationException;

            //we throw always configuration exceptions (historical)
            if (!exception.IsLoggedToInternalLogger())
            {
                var level = isConfigError ? LogLevel.Warn : LogLevel.Error;
                if (loggerContext != null)
                    InternalLogger.Log(exception, level, "{0}: Error has been raised.", loggerContext);
                else
                    InternalLogger.Log(exception, level, "Error has been raised.");
            }

            //if ThrowConfigExceptions == null, use  ThrowExceptions
            var shallRethrow = isConfigError ? (LogManager.ThrowConfigExceptions ?? LogManager.ThrowExceptions) : LogManager.ThrowExceptions;
            return shallRethrow;
        }

        /// <summary>
        /// Determines whether the exception must be rethrown immediately, without logging the error to the <see cref="InternalLogger"/>.
        ///
        /// Only used this method in special cases.
        /// </summary>
        /// <param name="exception">The exception to check.</param>
        /// <returns><c>true</c>if the <paramref name="exception"/> must be rethrown, <c>false</c> otherwise.</returns>
        public static bool MustBeRethrownImmediately(this Exception exception)
        {
#if !NETSTANDARD1_0
            if (exception is StackOverflowException)
            {
                return true;
            }

            if (exception is ThreadAbortException)
            {
                return true;
            }
#endif

            if (exception is OutOfMemoryException)
            {
                return true;
            }

#if DEBUG
            if (exception is InvalidCastException)
            {
                return true;
            }
            if (exception is NullReferenceException)
            {
                return true;
            }
            if (exception is DivideByZeroException)
            {
                return true;
            }
            if (exception is OverflowException)
            {
                return true;
            }
            if (exception is System.Net.WebException)
            {
                return false;   // Not a real InvalidOperationException
            }
            if (exception is InvalidOperationException)
            {
                return true;    // Ex. Collection was modified
            }
            if (exception is IndexOutOfRangeException)
            {
                return true;
            }
            if (exception is System.Reflection.TargetInvocationException)
            {
                return true;    // Compiler/reflection exception
            }
#endif
            return false;
        }
    }
}

Maybe add LoggingConfiguration as property to `IInternalLoggerContext`, and then extract the LogFactory from that? When InternalLogger-logging is improved with Nlog 5, then it will all work automagically.


@@ -9,7 +9,10 @@ namespace Datadog.Trace.Util
     internal static class DomainMetadata
     {
         private const string UnknownName = "unknown";
-        private static Process _currentProcess;
+        private static bool _initialized;
+        private static string _currentProcessName;
+        private static string _currentProcessMachineName;
+        private static int _currentProcessId;
         private static bool _processDataPoisoned;
         private static bool _domainDataPoisoned;
         private static bool? _isAppInsightsAppDomain;

using System;
using System.Diagnostics;

namespace Datadog.Trace.Util
{
    /// <summary>
    /// Dedicated helper class for consistently referencing Process and AppDomain information.
    /// </summary>
    internal static class DomainMetadata
    {
        private const string UnknownName = "unknown";
        private static Process _currentProcess;
        private static bool _processDataPoisoned;
        private static bool _domainDataPoisoned;
        private static bool? _isAppInsightsAppDomain;

        static DomainMetadata()
        {
            TrySetProcess();
        }

        public static string ProcessName
        {
            get
            {
                try
                {
                    return !_processDataPoisoned ? _currentProcess.ProcessName : UnknownName;
                }
                catch
                {
                    _processDataPoisoned = true;
                    return UnknownName;
                }
            }
        }

        public static string MachineName
        {
            get
            {
                try
                {
                    return !_processDataPoisoned ? _currentProcess.MachineName : UnknownName;
                }
                catch
                {
                    _processDataPoisoned = true;
                    return UnknownName;
                }
            }
        }

        public static int ProcessId
        {
            get
            {
                try
                {
                    return !_processDataPoisoned ? _currentProcess.Id : -1;
                }
                catch
                {
                    _processDataPoisoned = true;
                    return -1;
                }
            }
        }

        public static string AppDomainName
        {
            get
            {
                try
                {
                    return !_domainDataPoisoned ? AppDomain.CurrentDomain.FriendlyName : UnknownName;
                }
                catch
                {
                    _domainDataPoisoned = true;
                    return UnknownName;
                }
            }
        }

        public static int AppDomainId
        {
            get
            {
                try
                {
                    return !_domainDataPoisoned ? AppDomain.CurrentDomain.Id : -1;
                }
                catch
                {
                    _domainDataPoisoned = true;
                    return -1;
                }
            }
        }

        public static bool ShouldAvoidAppDomain()
        {
            if (_isAppInsightsAppDomain == null)
            {
                _isAppInsightsAppDomain = AppDomainName.IndexOf("ApplicationInsights", StringComparison.OrdinalIgnoreCase) >= 0;
            }

            return _isAppInsightsAppDomain.Value;
        }

        private static void TrySetProcess()
        {
            try
            {
                if (!_processDataPoisoned && _currentProcess == null)
                {
                    _currentProcess = Process.GetCurrentProcess();
                }
            }
            catch
            {
                _processDataPoisoned = true;
            }
        }
    }
}

Do we need `_processDataPoisoned`? Can the name or the id of the current process ever change? Or the machine name?


@@ -48,5 +48,20 @@ namespace OpenTelemetry.Exporter.Jaeger
         /// Gets or sets the tags that should be sent with telemetry.
         /// </summary>
         public IEnumerable<KeyValuePair<string, object>> ProcessTags { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether or not a batch should be sent to the Jaeger agent for each service. Default value: true.
+        /// </summary>
+        /// <remarks>
+        /// Jaeger UI will only detect & color dependency spans when both
+        /// the client & server processes report data to the same Jaeger
+        /// instance. For processes that make calls to non-instrumented services
+        /// (third parties, SQL, legacy systems, etc.) the
+        /// GenerateServiceSpecificBatches flag is provided to trick Jaeger into
+        /// correctly detecting and displaying all dependent spans as if both
+        /// sides reported data. For more details, see <a
+        /// href="https://github.com/jaegertracing/jaeger-ui/issues/594">jaeger-ui/issues/594</a>.
+        /// </remarks>
+        public bool GenerateServiceSpecificBatches { get; set; } = true;
     }
 }

// <copyright file="JaegerExporterOptions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System.Collections.Generic;

namespace OpenTelemetry.Exporter.Jaeger
{
    public class JaegerExporterOptions
    {
        internal const string DefaultServiceName = "OpenTelemetry Exporter";

        internal const int DefaultMaxPayloadSizeInBytes = 4096;

        /// <summary>
        /// Gets or sets the name of the service reporting telemetry. Default value: OpenTelemetry Exporter.
        /// </summary>
        public string ServiceName { get; set; } = DefaultServiceName;

        /// <summary>
        /// Gets or sets the Jaeger agent host. Default value: localhost.
        /// </summary>
        public string AgentHost { get; set; } = "localhost";

        /// <summary>
        /// Gets or sets the Jaeger agent "compact thrift protocol" port. Default value: 6831.
        /// </summary>
        public int AgentPort { get; set; } = 6831;

        /// <summary>
        /// Gets or sets the maximum payload size in bytes. Default value: 4096.
        /// </summary>
        public int? MaxPayloadSizeInBytes { get; set; } = DefaultMaxPayloadSizeInBytes;

        /// <summary>
        /// Gets or sets the tags that should be sent with telemetry.
        /// </summary>
        public IEnumerable<KeyValuePair<string, object>> ProcessTags { get; set; }
    }
}

Given there is a concern that this causes exporter to send "incorrect" data, should we make this default to "false", so that SDK by default exports correct data. If a user want to send "incorrect" data to have better UI experience, let them explicitly opt-in to this behavior?


@@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
             get
             {
                 var dataset = new TheoryData<H2SpecTestCase>();
-                var toSkip = new[] { "http2/5.1/8" };
+                var toSkip = new string[] { /*"http2/5.1/8"*/ };

                 foreach (var testcase in H2SpecCommands.EnumerateTestCases())
                 {

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#if NETCOREAPP2_2

using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Testing;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
{
    [OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/corefx/issues/30492")]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win81,
        SkipReason = "Missing Windows ALPN support: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation#Support")]
    public class H2SpecTests : TestApplicationErrorLoggerLoggedTest
    {
        [ConditionalTheory]
        [MemberData(nameof(H2SpecTestCases))]
        public async Task RunIndividualTestCase(H2SpecTestCase testCase)
        {
            var hostBuilder = TransportSelector.GetWebHostBuilder()
                .UseKestrel(options =>
                {
                    options.Listen(IPAddress.Loopback, 0, listenOptions =>
                    {
                        listenOptions.Protocols = HttpProtocols.Http2;
                        if (testCase.Https)
                        {
                            listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
                        }
                    });
                })
                .ConfigureServices(AddTestLogging)
                .Configure(ConfigureHelloWorld);

            using (var host = hostBuilder.Build())
            {
                await host.StartAsync();

                H2SpecCommands.RunTest(testCase.Id, host.GetPort(), testCase.Https, Logger);
            }
        }

        public static TheoryData<H2SpecTestCase> H2SpecTestCases
        {
            get
            {
                var dataset = new TheoryData<H2SpecTestCase>();
                var toSkip = new[] { "http2/5.1/8" };

                foreach (var testcase in H2SpecCommands.EnumerateTestCases())
                {
                    string skip = null;
                    if (toSkip.Contains(testcase.Item1))
                    {
                        skip = "https://github.com/aspnet/KestrelHttpServer/issues/2154";
                    }

                    dataset.Add(new H2SpecTestCase
                    {
                        Id = testcase.Item1,
                        Description = testcase.Item2,
                        Https = false,
                        Skip = skip,
                    });

                    dataset.Add(new H2SpecTestCase
                    {
                        Id = testcase.Item1,
                        Description = testcase.Item2,
                        Https = true,
                        Skip = skip,
                    });
                }

                return dataset;
            }
        }

        public class H2SpecTestCase : IXunitSerializable
        {
            // For the serializer
            public H2SpecTestCase()
            {
            }

            public string Id { get; set; }
            public string Description { get; set; }
            public bool Https { get; set; }
            public string Skip { get; set; }

            public void Deserialize(IXunitSerializationInfo info)
            {
                Id = info.GetValue<string>(nameof(Id));
                Description = info.GetValue<string>(nameof(Description));
                Https = info.GetValue<bool>(nameof(Https));
                Skip = info.GetValue<string>(nameof(Skip));
            }

            public void Serialize(IXunitSerializationInfo info)
            {
                info.AddValue(nameof(Id), Id, typeof(string));
                info.AddValue(nameof(Description), Description, typeof(string));
                info.AddValue(nameof(Https), Https, typeof(bool));
                info.AddValue(nameof(Skip), Skip, typeof(string));
            }

            public override string ToString()
            {
                return $"{Id}, HTTPS:{Https}, {Description}";
            }
        }

        private void ConfigureHelloWorld(IApplicationBuilder app)
        {
            app.Run(async context =>
            {
                // Read the whole request body to check for errors.
                await context.Request.Body.CopyToAsync(Stream.Null);
                await context.Response.WriteAsync("Hello World");
            });
        }
    }
}
#elif NET461 // HTTP/2 is not supported
#else
#error TFMs need updating
#endif


@@ -16,12 +16,13 @@
 //

 using Nethermind.Core;
+using Nethermind.TxPool;

 namespace Nethermind.Consensus.Transactions
 {
     public class NullTxFilter : ITxFilter
     {
-        public (bool Allowed, string Reason) IsAllowed(Transaction tx, BlockHeader parentHeader) => (true, string.Empty);
+        public (bool Allowed, AddTxResult? Reason) IsAllowed(Transaction tx, BlockHeader parentHeader) => (true, null);

         public static readonly NullTxFilter Instance = new();
     }

//  Copyright (c) 2021 Demerzel Solutions Limited
//  This file is part of the Nethermind library.
//
//  The Nethermind library is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  The Nethermind library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License
//  along with the Nethermind. If not, see <http://www.gnu.org/licenses/>.
//

using Nethermind.Core;

namespace Nethermind.Consensus.Transactions
{
    public class NullTxFilter : ITxFilter
    {
        public (bool Allowed, string Reason) IsAllowed(Transaction tx, BlockHeader parentHeader) => (true, string.Empty);

        public static readonly NullTxFilter Instance = new();
    }
}

As we have AddTxResult.Added, we don't need the tuple now, we can simplify this a lot.


@@ -3,14 +3,47 @@
 // See the LICENSE file in the project root for more information.

 using System;
+using Foundation;
 using MvvmCross.Core;
+using MvvmCross.ViewModels;
 using UIKit;

 namespace MvvmCross.Platform.Tvos.Core
 {
-    public class MvxApplicationDelegate : UIApplicationDelegate
-                                          , IMvxApplicationDelegate
+    public abstract class MvxApplicationDelegate : UIApplicationDelegate, IMvxApplicationDelegate
     {
+        private MvxTvosSetup _setup;
+        protected MvxTvosSetup Setup
+        {
+            get
+            {
+                if (_setup == null)
+                    _setup = CreateSetup(this, Window);
+                return _setup;
+            }
+        }
+
+        public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
+        {
+            Setup.Initialize();
+            RunAppStart(launchOptions);
+
+            FireLifetimeChanged(MvxLifetimeEvent.Launching);
+            return base.FinishedLaunching(application, launchOptions);
+        }
+
+        protected virtual void RunAppStart(object hint = null)
+        {
+            var startup = Mvx.Resolve<IMvxAppStart>();
+            if (!startup.IsStarted)
+                startup.Start(GetAppStartHint(hint));
+        }
+
+        protected virtual object GetAppStartHint(object hint = null)
+        {
+            return null;
+        }
+
         public override void WillEnterForeground(UIApplication application)
         {
             FireLifetimeChanged(MvxLifetimeEvent.ActivatedFromMemory);

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MS-PL license.
// See the LICENSE file in the project root for more information.

using System;
using MvvmCross.Core;
using UIKit;

namespace MvvmCross.Platform.Tvos.Core
{
    public class MvxApplicationDelegate : UIApplicationDelegate
                                          , IMvxApplicationDelegate
    {
        public override void WillEnterForeground(UIApplication application)
        {
            FireLifetimeChanged(MvxLifetimeEvent.ActivatedFromMemory);
        }

        public override void DidEnterBackground(UIApplication application)
        {
            FireLifetimeChanged(MvxLifetimeEvent.Deactivated);
        }

        public override void WillTerminate(UIApplication application)
        {
            FireLifetimeChanged(MvxLifetimeEvent.Closing);
        }

        public override void FinishedLaunching(UIApplication application)
        {
            FireLifetimeChanged(MvxLifetimeEvent.Launching);
        }

        private void FireLifetimeChanged(MvxLifetimeEvent which)
        {
            var handler = LifetimeChanged;
            handler?.Invoke(this, new MvxLifetimeEventArgs(which));
        }

        public event EventHandler<MvxLifetimeEventArgs> LifetimeChanged;
    }
}

Can we make tvOS work the same way as Android and iOS? (use a `InitializePlatform` method instead of ctor arguments) It seems cleaner to me to unify all setup changes on the same PR


@@ -174,6 +174,7 @@ namespace pwiz.Skyline.Model.Results
                     _completed(result, x == null ? _status : _status.ChangeErrorException(x));
                 }
                 catch (Exception x2)
+
                 {
                     _completed(null, _status.ChangeErrorException(x2));
                 }

/*
 * Original author: Brendan MacLean <brendanx .at. u.washington.edu>,
 *                  MacCoss Lab, Department of Genome Sciences, UW
 *
 * Copyright 2009 University of Washington - Seattle, WA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.IO;
using pwiz.Common.Collections;
using pwiz.Common.SystemUtil;
using pwiz.Skyline.Properties;
using pwiz.Skyline.Util;
using pwiz.Skyline.Util.Extensions;

namespace pwiz.Skyline.Model.Results
{
    internal class ChromCacheWriter : IDisposable
    {
        private readonly Action<ChromatogramCache, IProgressStatus> _completed;

        protected List<ChromCachedFile> _listCachedFiles = new List<ChromCachedFile>();
        protected BlockedArrayList<ChromTransition> _listTransitions =
            new BlockedArrayList<ChromTransition>(ChromTransition.SizeOf, ChromTransition.DEFAULT_BLOCK_SIZE);
        protected BlockedArrayList<ChromGroupHeaderEntry> _listGroups =
            new BlockedArrayList<ChromGroupHeaderEntry>(ChromGroupHeaderInfo.SizeOf, ChromGroupHeaderInfo.DEFAULT_BLOCK_SIZE);
        protected List<byte> _listTextIdBytes = new List<byte>();
        protected readonly List<Type> _listScoreTypes = new List<Type>();
        protected readonly FileSaver _fs;
        protected readonly FileSaver _fsScans;
        protected readonly FileSaver _fsPeaks;
        protected readonly FileSaver _fsScores;
        protected readonly ILoadMonitor _loader;
        protected IProgressStatus _status;
        protected int _peakCount;
        protected int _scoreCount;
        protected IPooledStream _destinationStream;

        protected ChromCacheWriter(string cachePath, ILoadMonitor loader, IProgressStatus status,
                                   Action<ChromatogramCache, IProgressStatus> completed)
        {
            CachePath = cachePath;
            CacheFormat = CacheFormat.CURRENT;
            _fs = new FileSaver(CachePath);
            _fsScans = new FileSaver(CachePath + ChromatogramCache.SCANS_EXT, true);
            _fsPeaks = new FileSaver(CachePath + ChromatogramCache.PEAKS_EXT, true);
            _fsScores = new FileSaver(CachePath + ChromatogramCache.SCORES_EXT, true);
            _loader = loader;
            _status = status;
            _completed = completed;
        }

        protected string CachePath { get; private set; }

        public CacheFormat CacheFormat { get; protected set; }

        protected void Complete(Exception x)
        {
            lock (this)
            {
                ChromatogramCache result = null;
                try
                {
                    if (x == null && !_status.IsFinal)
                    {
                        long locationScanIds = 0, countBytesScanIds = 0;
                        if (_fs.Stream != null)
                        {
                            try
                            {
                                locationScanIds = _fs.Stream.Position;
                                countBytesScanIds = _fsScans.Stream.Position;

                                _listGroups.Sort();
                                var listChromGroupHeaderInfos = ReadOnlyList.Create(_listGroups.Count,
                                    i => _listGroups[i].ChromGroupHeaderInfo);
                                ChromatogramCache.WriteStructs(CacheFormat,
                                                               _fs.Stream,
                                                               _fsScans.Stream,
                                                               _fsPeaks.Stream,
                                                               _fsScores.Stream,
                                                               _listCachedFiles,
                                                               listChromGroupHeaderInfos,
                                                               _listTransitions,
                                                               _listTextIdBytes,
                                                               _listScoreTypes,
                                                               _scoreCount,
                                                               _peakCount);

                                _loader.StreamManager.Finish(_fs.Stream);
                                _fs.Stream = null;
                                // Allow cancellation right up to the final commit
                                if (!_loader.IsCanceled)
                                    _fs.Commit(_destinationStream);
                                else
                                {
                                    _loader.UpdateProgress(_status = _status.Cancel());
                                }
                            }
                            catch (Exception xWrite)
                            {
                                throw new IOException(TextUtil.LineSeparate(string.Format(Resources.ChromCacheWriter_Complete_Failure_attempting_to_write_the_file__0_, _fs.RealName), xWrite.Message));
                            }
                        }

                        if (!_status.IsCanceled)
                        {
                            // Create stream identifier, but do not open.  The stream will be opened
                            // the first time the document uses it.
                            var readStream = _loader.StreamManager.CreatePooledStream(CachePath, false);

                            // DebugLog.Info("{0}. {1} - created", readStream.GlobalIndex, CachePath);

                            _fsPeaks.Stream.Seek(0, SeekOrigin.Begin);
                            _fsScores.Stream.Seek(0, SeekOrigin.Begin);
                            var arrayCachFiles = _listCachedFiles.ToArray();
                            _listCachedFiles = null;
                            var arrayChromEntries = BlockedArray<ChromGroupHeaderInfo>.Convert(_listGroups,
                                entry => entry.ChromGroupHeaderInfo);
                            _listGroups = null;
                            var arrayTransitions = _listTransitions.ToBlockedArray();
                            _listTransitions = null;
                            var peakSerializer = CacheFormat.ChromPeakSerializer();
                            var chromPeaks = new BlockedArray<ChromPeak>(
                                count => peakSerializer.ReadArray(_fsPeaks.FileStream, count), _peakCount,
                                ChromPeak.SizeOf, ChromPeak.DEFAULT_BLOCK_SIZE);
                            var scores = new BlockedArray<float>(
                                count => PrimitiveArrays.Read<float>(_fsScores.FileStream, count), _scoreCount,
                                sizeof(float), ChromatogramCache.DEFAULT_SCORES_BLOCK_SIZE);
                            var textIdBytes = _listTextIdBytes.ToArray();
                            _listTextIdBytes = null;

                            var rawData = new ChromatogramCache.RawData(CacheFormat)
                            {
                                ChromCacheFiles = arrayCachFiles,
                                ChromatogramEntries = arrayChromEntries,
                                ChromTransitions = arrayTransitions,
                                ChromatogramPeaks = chromPeaks,
                                ScoreTypes = _listScoreTypes.ToArray(),
                                Scores = scores,
                                TextIdBytes = textIdBytes,
                                CountBytesScanIds = countBytesScanIds,
                                LocationScanIds = locationScanIds
                            };
                            result = new ChromatogramCache(CachePath, rawData, readStream);
                            _status = _status.Complete();
                            _loader.UpdateProgress(_status);
                        }
                    }
                }
                catch (Exception x2)
                {
                    x = x2;
                }
                finally
                {
                    Dispose();
                }

                try
                {
                    _completed(result, x == null ? _status : _status.ChangeErrorException(x));
                }
                catch (Exception x2)
                {
                    _completed(null, _status.ChangeErrorException(x2));
                }
            }
        }

        public virtual void Dispose()
        {
            if (_fs.Stream != null)
            {
                try { _loader.StreamManager.Finish(_fs.Stream); }
                catch (IOException) { }
                _fs.Stream = null;
            }
            _fs.Dispose();
            _fsPeaks.Dispose();
            _fsScans.Dispose();
            _fsScores.Dispose();
        }
    }
}


@@ -16,10 +16,16 @@
 //

 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Reflection;
 using FluentAssertions;
+using Nethermind.Core;
+using Nethermind.Logging;
+using Nethermind.Monitoring.Config;
+using Nethermind.Monitoring.Metrics;
+using Nethermind.Runner;
 using NUnit.Framework;

 namespace Nethermind.Monitoring.Test

//  Copyright (c) 2021 Demerzel Solutions Limited
//  This file is part of the Nethermind library.
//
//  The Nethermind library is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  The Nethermind library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License
//  along with the Nethermind. If not, see <http://www.gnu.org/licenses/>.
//

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using FluentAssertions;
using NUnit.Framework;

namespace Nethermind.Monitoring.Test
{
    [TestFixture]
    public class MetricsTests
    {
        [Test]
        public void All_config_items_have_descriptions()
        {
            ValidateMetricsDescriptions();
        }

        public static void ValidateMetricsDescriptions()
        {
            ForEachProperty(CheckDescribedOrHidden);
        }

        private static void CheckDescribedOrHidden(PropertyInfo property)
        {
            System.ComponentModel.DescriptionAttribute attribute = property.GetCustomAttribute<System.ComponentModel.DescriptionAttribute>();
            attribute.Should().NotBeNull();
        }

        private static void ForEachProperty(Action<PropertyInfo> verifier)
        {
            string[] dlls = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "Nethermind.*.dll");
            foreach (string dll in dlls)
            {
                TestContext.WriteLine($"Verify {nameof(MetricsTests)} on {Path.GetFileName(dll)}");
                Assembly assembly = Assembly.LoadFile(dll);
                Type[] configs = assembly.GetExportedTypes().Where(t => t.Name == "Metrics").ToArray();

                foreach (Type metricsType in configs)
                {
                    TestContext.WriteLine($"  Verifying type {metricsType.FullName}");
                    PropertyInfo[] properties = metricsType.GetProperties(BindingFlags.Static | BindingFlags.Public);
                    foreach (PropertyInfo property in properties)
                    {
                        try
                        {
                            TestContext.WriteLine($"    Verifying property {property.Name}");
                            verifier(property);
                        }
                        catch (Exception e)
                        {
                            throw new Exception(property.Name, e);
                        }
                    }
                }
            }
        }
    }
}

minor: typo (knowMetricsTypes -> knownMetricsTypes)


@@ -46,13 +46,13 @@ namespace OpenTelemetry.Extensions.Hosting.Implementation
             }
         }

-        [Event(1, Message = "Failed to initialize: '{0}'. OpenTelemetry will not work.", Level = EventLevel.Error)]
+        [Event(1, Message = "An exception occured while adding OpenTelemetry Tracing to ServiceCollection. OpenTelemetry tracing will not work. Exception: '{0}'.", Level = EventLevel.Error)]
         public void FailedInitialize(string exception)
         {
             this.WriteEvent(1, exception);
         }

-        [Event(2, Message = "Failed to get OpenTelemetrySDK: '{0}'. OpenTelemetry will not work.", Level = EventLevel.Error)]
+        [Event(2, Message = "An exception occured while retrieving OpenTelemetry Tracer from Service Provider. OpenTelemetry tracing will not work. Exception: '{0}'.", Level = EventLevel.Error)]
         public void FailedOpenTelemetrySDK(string exception)
         {
             this.WriteEvent(2, exception);

// <copyright file="HostingExtensionsEventSource.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using System.Diagnostics.Tracing;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Extensions.Hosting.Implementation
{
    /// <summary>
    /// EventSource events emitted from the project.
    /// </summary>
    [EventSource(Name = "OpenTelemetry-Extensions-Hosting")]
    internal class HostingExtensionsEventSource : EventSource
    {
        public static HostingExtensionsEventSource Log = new HostingExtensionsEventSource();

        [NonEvent]
        public void FailedInitialize(Exception ex)
        {
            if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1)))
            {
                this.FailedInitialize(ex.ToInvariantString());
            }
        }

        [NonEvent]
        public void FailedOpenTelemetrySDK(Exception ex)
        {
            if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1)))
            {
                this.FailedOpenTelemetrySDK(ex.ToInvariantString());
            }
        }

        [Event(1, Message = "Failed to initialize: '{0}'. OpenTelemetry will not work.", Level = EventLevel.Error)]
        public void FailedInitialize(string exception)
        {
            this.WriteEvent(1, exception);
        }

        [Event(2, Message = "Failed to get OpenTelemetrySDK: '{0}'. OpenTelemetry will not work.", Level = EventLevel.Error)]
        public void FailedOpenTelemetrySDK(string exception)
        {
            this.WriteEvent(2, exception);
        }
    }
}

Happy to get suggestion for better messaging here. Here's what I intended to convey: Something is wrong - the Exception is a hint to what might be the cause The impact of that - tracing wont work.


@@ -126,6 +126,8 @@ namespace Nethermind.Synchronization.ParallelSync
         }

         public long FindBestHeader() => _blockTree.BestSuggestedHeader?.Number ?? 0;
+
+        public Keccak FindBestHeaderHash() => _blockTree.BestSuggestedHeader?.Hash ?? Keccak.Zero;

         public long FindBestFullBlock() => Math.Min(FindBestHeader(), _blockTree.BestSuggestedBody?.Number ?? 0); // avoiding any potential concurrency issue

//  Copyright (c) 2018 Demerzel Solutions Limited
//  This file is part of the Nethermind library.
//
//  The Nethermind library is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  The Nethermind library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License
//  along with the Nethermind. If not, see <http://www.gnu.org/licenses/>.

using System;
using Nethermind.Blockchain;
using Nethermind.Blockchain.Receipts;
using Nethermind.Blockchain.Synchronization;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Db;
using Nethermind.Dirichlet.Numerics;
using Nethermind.Logging;

namespace Nethermind.Synchronization.ParallelSync
{
    public class SyncProgressResolver : ISyncProgressResolver
    {
        // TODO: we can search 1024 back and confirm 128 deep header and start using it as Max(0, confirmed)
        // then we will never have to look 128 back again
        // note that we will be doing that every second or so
        private const int _maxLookupBack = 128;

        private readonly IBlockTree _blockTree;
        private readonly IReceiptStorage _receiptStorage;
        private readonly IDb _stateDb;
        private readonly IDb _beamStateDb;
        private readonly ISyncConfig _syncConfig;
        private ILogger _logger;

        public SyncProgressResolver(IBlockTree blockTree, IReceiptStorage receiptStorage, IDb stateDb, IDb beamStateDb, ISyncConfig syncConfig, ILogManager logManager)
        {
            _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager));
            _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree));
            _receiptStorage = receiptStorage ?? throw new ArgumentNullException(nameof(receiptStorage));
            _stateDb = stateDb ?? throw new ArgumentNullException(nameof(stateDb));
            _beamStateDb = beamStateDb ?? throw new ArgumentNullException(nameof(beamStateDb));
            _syncConfig = syncConfig ?? throw new ArgumentNullException(nameof(syncConfig));
        }

        private bool IsFullySynced(Keccak stateRoot)
        {
            if (stateRoot == Keccak.EmptyTreeHash)
            {
                return true;
            }

            return _stateDb.Innermost.Get(stateRoot) != null;
        }

        private bool IsBeamSynced(Keccak stateRoot)
        {
            if (stateRoot == Keccak.EmptyTreeHash)
            {
                return true;
            }

            return _beamStateDb.Innermost.Get(stateRoot) != null;
        }

        public long FindBestFullState()
        {
            // so the full state can be in a few places but there are some best guesses
            // if we are state syncing then the full state may be one of the recent blocks (maybe one of the last 128 blocks)
            // if we full syncing then the state should be at head
            // if we are beam syncing then the state should be in a different DB and should not cause much trouble here
            // it also may seem tricky if best suggested is part of a reorg while we are already full syncing so
            // ideally we would like to check it siblings too (but this may be a bit expensive and less likely
            // to be important
            // we want to avoid a scenario where state is not found even as it is just near head or best suggested

            Block head = _blockTree.Head;
            BlockHeader initialBestSuggested = _blockTree.BestSuggestedHeader; // just storing here for debugging sake
            BlockHeader bestSuggested = initialBestSuggested;

            long bestFullState = 0;
            if (head != null)
            {
                // head search should be very inexpensive as we generally expect the state to be there
                bestFullState = SearchForFullState(head.Header);
            }

            if (bestSuggested != null)
            {
                if (bestFullState < bestSuggested?.Number)
                {
                    bestFullState = Math.Max(bestFullState, SearchForFullState(bestSuggested));
                }
            }

            return bestFullState;
        }

        private long SearchForFullState(BlockHeader startHeader)
        {
            long bestFullState = 0;
            for (int i = 0; i < _maxLookupBack; i++)
            {
                if (startHeader == null)
                {
                    break;
                }

                if (IsFullySynced(startHeader.StateRoot))
                {
                    bestFullState = startHeader.Number;
                    break;
                }

                startHeader = _blockTree.FindHeader(startHeader.ParentHash, BlockTreeLookupOptions.TotalDifficultyNotNeeded);
            }

            return bestFullState;
        }

        public long FindBestHeader() => _blockTree.BestSuggestedHeader?.Number ?? 0;

        public long FindBestFullBlock() => Math.Min(FindBestHeader(), _blockTree.BestSuggestedBody?.Number ?? 0); // avoiding any potential concurrency issue

        public bool IsLoadingBlocksFromDb()
        {
            return !_blockTree.CanAcceptNewBlocks;
        }

        public long FindBestProcessedBlock() => _blockTree.Head?.Number ?? -1;

        public UInt256 ChainDifficulty => _blockTree.BestSuggestedBody?.TotalDifficulty ?? UInt256.Zero;

        public bool IsFastBlocksHeadersFinished() => !IsFastBlocks() || (_blockTree.LowestInsertedHeader?.Number ?? long.MaxValue) <= 1;

        public bool IsFastBlocksBodiesFinished() => !IsFastBlocks() || (!_syncConfig.DownloadBodiesInFastSync || (_blockTree.LowestInsertedBody?.Number ?? long.MaxValue) <= 1);

        public bool IsFastBlocksReceiptsFinished() => !IsFastBlocks() || (!_syncConfig.DownloadReceiptsInFastSync || (_receiptStorage.LowestInsertedReceiptBlock ?? long.MaxValue) <= 1);

        private bool IsFastBlocks()
        {
            bool isFastBlocks = _syncConfig.FastBlocks;

            // if pivot number is 0 then it is equivalent to fast blocks disabled
            if (!isFastBlocks || _syncConfig.PivotNumberParsed == 0L)
            {
                return false;
            }

            bool immediateBeamSync = !_syncConfig.DownloadHeadersInFastSync;
            bool anyHeaderDownloaded = _blockTree.LowestInsertedHeader != null;
            if (immediateBeamSync && anyHeaderDownloaded)
            {
                return false;
            }

            return true;
        }

    }
}

Keccak.Zero should not be used to mean null


@@ -47,13 +47,22 @@ namespace OpenTelemetry.Logs
             {
                 var options = this.provider.Options;

+                string formattedMessage = null;
+                if (options.IncludeFormattedMessage)
+                {
+                    if (formatter != null)
+                    {
+                        formattedMessage = formatter(state, exception);
+                    }
+                }
+
                 var record = new LogRecord(
                     options.IncludeScopes ? this.ScopeProvider : null,
                     DateTime.UtcNow,
                     this.categoryName,
                     logLevel,
                     eventId,
-                    options.IncludeFormattedMessage ? formatter(state, exception) : null,
+                    formattedMessage,
                     options.ParseStateValues ? null : (object)state,
                     exception,
                     options.ParseStateValues ? this.ParseState(state) : null);

// <copyright file="OpenTelemetryLogger.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Logging;

namespace OpenTelemetry.Logs
{
    internal class OpenTelemetryLogger : ILogger
    {
        private readonly string categoryName;
        private readonly OpenTelemetryLoggerProvider provider;

        internal OpenTelemetryLogger(string categoryName, OpenTelemetryLoggerProvider provider)
        {
            this.categoryName = categoryName ?? throw new ArgumentNullException(nameof(categoryName));
            this.provider = provider ?? throw new ArgumentNullException(nameof(provider));
        }

        internal IExternalScopeProvider ScopeProvider { get; set; }

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (!this.IsEnabled(logLevel)
                || Sdk.SuppressInstrumentation)
            {
                return;
            }

            var processor = this.provider.Processor;
            if (processor != null)
            {
                var options = this.provider.Options;

                var record = new LogRecord(
                    options.IncludeScopes ? this.ScopeProvider : null,
                    DateTime.UtcNow,
                    this.categoryName,
                    logLevel,
                    eventId,
                    options.IncludeFormattedMessage ? formatter(state, exception) : null,
                    options.ParseStateValues ? null : (object)state,
                    exception,
                    options.ParseStateValues ? this.ParseState(state) : null);

                processor.OnEnd(record);

                record.ScopeProvider = null;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsEnabled(LogLevel logLevel)
        {
            return logLevel != LogLevel.None;
        }

        public IDisposable BeginScope<TState>(TState state) => this.ScopeProvider?.Push(state) ?? null;

        private IReadOnlyList<KeyValuePair<string, object>> ParseState<TState>(TState state)
        {
            if (state is IReadOnlyList<KeyValuePair<string, object>> stateList)
            {
                return stateList;
            }
            else if (state is IEnumerable<KeyValuePair<string, object>> stateValues)
            {
                return new List<KeyValuePair<string, object>>(stateValues);
            }
            else
            {
                return new List<KeyValuePair<string, object>>
                {
                    new KeyValuePair<string, object>(string.Empty, state),
                };
            }
        }
    }
}

Could probably just do: `options.IncludeFormattedMessage ? formatter?.Invoke(state, exception) : null`


@@ -53,7 +53,34 @@ namespace pwiz.Common.Chemistry
             {"Thr",'T'},
             {"Trp",'W'},
             {"Tyr",'Y'},
-            {"Val",'V'}
+            {"Val",'V'},
+            {"Sec",'U'},
+            {"Pyl",'O'}
+        });
+        public static readonly IDictionary<char, String> FullNames = new ImmutableDictionary<char, String>(
+            new Dictionary<char, string> {
+            {'A',"Alanine"},
+            {'R', "Arginine"},
+            {'N', "Asparagine"},
+            {'D', "Aspartic acid"},
+            {'C', "Cysteine"},
+            {'E', "Glutamic acid"},
+            {'Q', "Glutamine"},
+            {'G', "Glycine"},
+            {'H', "Histidine"},
+            {'I', "Isoleucine"},
+            {'L', "Leucine"},
+            {'K', "Lysine"},
+            {'M', "Methionine"},
+            {'F', "Phenylalanine"},
+            {'P', "Proline"},
+            {'S', "Serine"},
+            {'T', "Threonine"},
+            {'W', "Tryptophan"},
+            {'Y', "Tyrosine"},
+            {'V', "Valine"},
+            {'U', "Selenocysteine"},
+            {'O', "Pyrrolysine"}
         });
         // ReSharper restore LocalizableElement

/*
 * Original author: Nicholas Shulman <nicksh .at. u.washington.edu>,
 *                  MacCoss Lab, Department of Genome Sciences, UW
 *
 * Copyright 2009 University of Washington - Seattle, WA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using pwiz.Common.Collections;
using pwiz.Common.SystemUtil;

namespace pwiz.Common.Chemistry
{
    public class AminoAcidFormulas : Immutable
    {
// ReSharper disable InconsistentNaming
        // public const double ProtonMass = 1.007276466879;  // per http://physics.nist.gov/cgi-bin/cuu/Value?mpu|search_for=proton+mass 12/18/2016
        public const double ProtonMass = 1.00727649;

        // ReSharper disable LocalizableElement
        public static readonly IDictionary<String, char> LongNames = new ImmutableDictionary<String,char>(
            new Dictionary<string, char> {
            {"Ala",'A'},
            {"Arg",'R'},
            {"Asn",'N'},
            {"Asp",'D'},
            {"Cys",'C'},
            {"Glu",'E'},
            {"Gln",'Q'},
            {"Gly",'G'},
            {"His",'H'},
            {"Ile",'I'},
            {"Leu",'L'},
            {"Lys",'K'},
            {"Met",'M'},
            {"Phe",'F'},
            {"Pro",'P'},
            {"Ser",'S'},
            {"Thr",'T'},
            {"Trp",'W'},
            {"Tyr",'Y'},
            {"Val",'V'}
        });
        // ReSharper restore LocalizableElement

        // ReSharper disable LocalizableElement
        public static readonly IDictionary<char, String> DefaultFormulas = new ImmutableDictionary<char, String>
            (
            new Dictionary<char, string>
                {
                    {'A', "C3H5ON"},
                    {'C', "C3H5ONS"},
                    {'D', "C4H5O3N"},
                    {'E', "C5H7O3N"},
                    {'F', "C9H9ON"},
                    {'G', "C2H3ON"},
                    {'H', "C6H7ON3"},
                    {'I', "C6H11ON"},
                    {'K', "C6H12ON2"},
                    {'L', "C6H11ON"},
                    {'M', "C5H9ONS"},
                    {'N', "C4H6O2N2"},
                    {'O', "C12H19N3O2"},
                    {'P', "C5H7ON"},
                    {'Q', "C5H8O2N2"},
                    {'R', "C6H12ON4"},
                    {'S', "C3H5O2N"},
                    {'T', "C4H7O2N"},
                    {'U', "C3H5NOSe"},
                    {'V', "C5H9ON"},
                    {'W', "C11H10ON2"},
                    {'Y', "C9H9O2N"},
                }
                );
        // ReSharper restore LocalizableElement

        private Dictionary<char, Molecule> _molecules;
        private static readonly Molecule H2O = Molecule.Parse(@"H2O");

        public static readonly AminoAcidFormulas Default = new AminoAcidFormulas
                                                               {
                                                                   MassShifts = new ImmutableDictionary<char,double>(new Dictionary<char, double>()),
                                                                   _molecules = DefaultFormulas.ToDictionary(kvp=>kvp.Key, kvp=>Molecule.Parse(kvp.Value)),
                                                                   IsotopeAbundances = IsotopeAbundances.Default,
                                                                   MassResolution = .001,
                                                               };
// ReSharper restore InconsistentNaming

        public double MassResolution { get; private set; }
        public IDictionary<char, double> MassShifts { get; private set; }
        public IsotopeAbundances IsotopeAbundances { get; private set; }
        public AminoAcidFormulas SetFormula(char aminoAcid, String formula)
        {
            return ChangeProp(ImClone(this), im =>
            {
                im._molecules = new Dictionary<char, Molecule>(_molecules);
                im._molecules[aminoAcid] = Molecule.Parse(formula);
            });
        }
        public AminoAcidFormulas SetMassShift(char aminoAcid, double massShift)
        {
            return SetMassShifts(new Dictionary<char, double> {{aminoAcid, massShift}});
        }
        public AminoAcidFormulas SetMassShifts(Dictionary<char,double> dictionary)
        {
            var newMassShifts = new Dictionary<char, double>(MassShifts);
            foreach (var entry in dictionary)
            {
                newMassShifts[entry.Key] = entry.Value;
            }

            return ChangeProp(ImClone(this), im => im.MassShifts = newMassShifts);
        }
        public AminoAcidFormulas SetIsotopeAbundances(IsotopeAbundances newAbundances)
        {
            return ChangeProp(ImClone(this), im => im.IsotopeAbundances = newAbundances);
        }
        public MassDistribution GetMassDistribution(Molecule molecule, int charge)
        {
            var md = new MassDistribution(MassResolution, .00001);
            var result = md;
            foreach (var element in molecule)
            {
                result = result.Add(md.Add(IsotopeAbundances[element.Key]).Multiply(element.Value));
            }
            if (charge != 0)
            {
                result = result.OffsetAndDivide(charge * ProtonMass, charge);
            }
            return result;
        }
        public AminoAcidFormulas SetMassResolution(double massResolution)
        {
            return ChangeProp(ImClone(this), im=>im.MassResolution = massResolution);
        }
        public MassDistribution GetMassDistribution(String peptide, int charge)
        {
            return GetMassDistribution(GetFormula(peptide), charge)
                .OffsetAndDivide(GetMassShift(peptide)/Math.Max(charge,1), 1);
        }
        public Molecule GetFormula(String peptide)
        {
            var formulas = peptide.Select(aa =>
                _molecules.TryGetValue(aa, out Molecule aaFormula) ? aaFormula : Molecule.Empty
            ).Append(H2O);
            return Molecule.Sum(formulas);
        }

        [CanBeNull]
        public Molecule GetAminoAcidFormula(char aa)
        {
            Molecule molecule;
            _molecules.TryGetValue(aa, out molecule);
            return molecule;
        }

        public Double GetMassShift(String peptide)
        {
            var result = 0.0;
            foreach (var ch in peptide)
            {
                double shift;
                MassShifts.TryGetValue(ch, out shift);
                result += shift;
            }
            return result;
        }
        public double GetMonoisotopicMass(String peptide)
        {
            double result = GetMassShift(peptide);
            foreach (var element in GetFormula(peptide))
            {
                result += IsotopeAbundances[element.Key].MostAbundanceMass*element.Value;
            }
            return result;
        }
    }
}

You should also add these new amino acids to "DefaultFormulas"


@@ -40,7 +40,9 @@ namespace Nethermind.Runner
             ContractResolver = new CamelCasePropertyNamesContractResolver()
         };

-        private IJsonSerializer _jsonSerializer = new EthereumJsonSerializer();
+        private IJsonSerializer _jsonSerializer = CreateJsonSerializer();
+
+        private static EthereumJsonSerializer CreateJsonSerializer() => new EthereumJsonSerializer(NullValueHandling.Include);

         public void ConfigureServices(IServiceCollection services)
         {

//  Copyright (c) 2018 Demerzel Solutions Limited
//  This file is part of the Nethermind library.
//
//  The Nethermind library is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  The Nethermind library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License
//  along with the Nethermind. If not, see <http://www.gnu.org/licenses/>.

using System;
using System.IO;
using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Nethermind.Config;
using Nethermind.Core.Attributes;
using Nethermind.JsonRpc;
using Nethermind.Serialization.Json;
using Nethermind.WebSockets;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace Nethermind.Runner
{
    public class Startup
    {
        private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };

        private IJsonSerializer _jsonSerializer = new EthereumJsonSerializer();

        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<KestrelServerOptions>(options => { options.AllowSynchronousIO = true; });
            Bootstrap.Instance.RegisterJsonRpcServices(services);
            var corsOrigins = Environment.GetEnvironmentVariable("NETHERMIND_CORS_ORIGINS") ?? "*";
            services.AddCors(c => c.AddPolicy("Cors",
                p => p.AllowAnyMethod().AllowAnyHeader().WithOrigins(corsOrigins)));
        }

        [Todo(Improve.Performance, "Can we write immediately to the stream instead of calling ToString on the entire JSON content?")]
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IJsonRpcProcessor jsonRpcProcessor,
            IJsonRpcService jsonRpcService)
        {
            _jsonSerializer = new EthereumJsonSerializer();
            foreach (JsonConverter converter in jsonRpcService.Converters)
            {
                _jsonSerializer.RegisterConverter(converter);
            }

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors("Cors");

            var configProvider = app.ApplicationServices.GetService<IConfigProvider>();
            var initConfig = configProvider.GetConfig<IInitConfig>();
            var jsonRpcConfig = configProvider.GetConfig<IJsonRpcConfig>();
            if (initConfig.WebSocketsEnabled)
            {
                app.UseWebSockets();
                app.UseWhen(ctx => ctx.WebSockets.IsWebSocketRequest
                                   && ctx.Connection.LocalPort == jsonRpcConfig.WebSocketsPort,
                builder => builder.UseWebSocketsModules());
            }

            app.Use(async (ctx, next) =>
            {
                if (ctx.Request.Method == "GET")
                {
                    await ctx.Response.WriteAsync("Nethermind JSON RPC");
                }
                else if (ctx.Connection.LocalPort == jsonRpcConfig.Port && ctx.Request.Method == "POST")
                {
                    using var reader = new StreamReader(ctx.Request.Body, Encoding.UTF8);
                    var request = await reader.ReadToEndAsync();
                    var result = await jsonRpcProcessor.ProcessAsync(request);

                    if (result.IsCollection)
                    {
                        _jsonSerializer.Serialize(ctx.Response.Body, result.Responses);
                    }
                    else
                    {
                        _jsonSerializer.Serialize(ctx.Response.Body, result.Response);
                    }

                    await ctx.Response.CompleteAsync();
                }
            });
        }
    }
}

this cannot be this way - there are plenty of places where they complain about nulls - it has to be done specifically on some properties only


@@ -52,6 +52,8 @@ namespace Nethermind.Runner.Ethereum.Steps

                 JsonRpcService jsonRpcService = new(_api.RpcModuleProvider, _api.LogManager);

+                _api.EthereumJsonSerializer.RegisterConverters(jsonRpcService.Converters);
+
                 JsonRpcProcessor jsonRpcProcessor = new(
                     jsonRpcService,
                     _api.EthereumJsonSerializer,

//  Copyright (c) 2021 Demerzel Solutions Limited
//  This file is part of the Nethermind library.
//
//  The Nethermind library is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  The Nethermind library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License
//  along with the Nethermind. If not, see <http://www.gnu.org/licenses/>.
//

using System.Threading;
using System.Threading.Tasks;
using Nethermind.Api;
using Nethermind.Core;
using Nethermind.Init.Steps;
using Nethermind.JsonRpc;
using Nethermind.JsonRpc.WebSockets;
using Nethermind.Logging;
using Nethermind.Runner.JsonRpc;

namespace Nethermind.Runner.Ethereum.Steps
{
    [RunnerStepDependencies(typeof(InitializeNetwork), typeof(RegisterRpcModules), typeof(SetupHive))]
    public class StartRpc : IStep
    {
        private readonly INethermindApi _api;

        public StartRpc(INethermindApi api)
        {
            _api = api;
        }

        public async Task Execute(CancellationToken cancellationToken)
        {
            IJsonRpcConfig jsonRpcConfig = _api.Config<IJsonRpcConfig>();
            ILogger logger = _api.LogManager.GetClassLogger();

            if (jsonRpcConfig.Enabled)
            {
                IInitConfig initConfig = _api.Config<IInitConfig>();
                JsonRpcLocalStats jsonRpcLocalStats = new(
                    _api.Timestamper,
                    jsonRpcConfig,
                    _api.LogManager);

                JsonRpcService jsonRpcService = new(_api.RpcModuleProvider, _api.LogManager);

                JsonRpcProcessor jsonRpcProcessor = new(
                    jsonRpcService,
                    _api.EthereumJsonSerializer,
                    jsonRpcConfig,
                    _api.FileSystem,
                    _api.LogManager);

                if (initConfig.WebSocketsEnabled)
                {
                    JsonRpcWebSocketsModule webSocketsModule = new (jsonRpcProcessor, jsonRpcService, jsonRpcLocalStats, _api.LogManager, _api.EthereumJsonSerializer);
                    _api.WebSocketsManager!.AddModule(webSocketsModule, true);
                }

                Bootstrap.Instance.JsonRpcService = jsonRpcService;
                Bootstrap.Instance.LogManager = _api.LogManager;
                Bootstrap.Instance.JsonSerializer = _api.EthereumJsonSerializer;
                Bootstrap.Instance.JsonRpcLocalStats = jsonRpcLocalStats;
                JsonRpcRunner? jsonRpcRunner = new(
                    jsonRpcProcessor,
                    _api.WebSocketsManager!,
                    _api.ConfigProvider,
                    _api.LogManager,
                    _api);

                await jsonRpcRunner.Start(cancellationToken).ContinueWith(x =>
                {
                    if (x.IsFaulted && logger.IsError)
                        logger.Error("Error during jsonRpc runner start", x.Exception);
                }, cancellationToken);

                JsonRpcIpcRunner jsonIpcRunner = new(jsonRpcProcessor, jsonRpcService, _api.ConfigProvider, _api.LogManager, jsonRpcLocalStats, _api.EthereumJsonSerializer, _api.FileSystem);
                jsonIpcRunner.Start(cancellationToken);

#pragma warning disable 4014
                _api.DisposeStack.Push(new Reactive.AnonymousDisposable(() => jsonRpcRunner.StopAsync())); // do not await
                _api.DisposeStack.Push(jsonIpcRunner); // do not await
#pragma warning restore 4014
            }
            else
            {
                if (logger.IsInfo) logger.Info("Json RPC is disabled");
            }
        }
    }
}

This change will register JSON RPC converters for the API serializer that is used in multiple places. Is it OK ? HTTP JSON RPC uses own instance of the serializer. Maybe we should do the same for the sockets or share the same instance with the HTTP module ?


@@ -12,8 +12,8 @@
 #include <nano/rpc/rpc_secure.hpp>
 #endif

-nano::rpc::rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a) :
-	config (config_a),
+nano::rpc::rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config config_a, nano::rpc_handler_interface & rpc_handler_interface_a) :
+	config (std::move (config_a)),
 	acceptor (io_ctx_a),
 	logger (std::chrono::milliseconds (0)),
 	io_ctx (io_ctx_a),

#include <nano/boost/asio/bind_executor.hpp>
#include <nano/lib/rpc_handler_interface.hpp>
#include <nano/lib/tlsconfig.hpp>
#include <nano/rpc/rpc.hpp>
#include <nano/rpc/rpc_connection.hpp>

#include <boost/format.hpp>

#include <iostream>

#ifdef NANO_SECURE_RPC
#include <nano/rpc/rpc_secure.hpp>
#endif

nano::rpc::rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a) :
	config (config_a),
	acceptor (io_ctx_a),
	logger (std::chrono::milliseconds (0)),
	io_ctx (io_ctx_a),
	rpc_handler_interface (rpc_handler_interface_a)
{
	rpc_handler_interface.rpc_instance (*this);
}

nano::rpc::~rpc ()
{
	if (!stopped)
	{
		stop ();
	}
}

void nano::rpc::start ()
{
	auto endpoint (boost::asio::ip::tcp::endpoint (boost::asio::ip::make_address_v6 (config.address), config.port));
	bool const is_loopback = (endpoint.address ().is_loopback () || (endpoint.address ().to_v6 ().is_v4_mapped () && boost::asio::ip::make_address_v4 (boost::asio::ip::v4_mapped, endpoint.address ().to_v6 ()).is_loopback ()));
	if (!is_loopback && config.enable_control)
	{
		auto warning = boost::str (boost::format ("WARNING: control-level RPCs are enabled on non-local address %1%, potentially allowing wallet access outside local computer") % endpoint.address ().to_string ());
		std::cout << warning << std::endl;
		logger.always_log (warning);
	}
	acceptor.open (endpoint.protocol ());
	acceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true));

	boost::system::error_code ec;
	acceptor.bind (endpoint, ec);
	if (ec)
	{
		logger.always_log (boost::str (boost::format ("Error while binding for RPC on port %1%: %2%") % endpoint.port () % ec.message ()));
		throw std::runtime_error (ec.message ());
	}
	acceptor.listen ();
	accept ();
}

void nano::rpc::accept ()
{
	auto connection (std::make_shared<nano::rpc_connection> (config, io_ctx, logger, rpc_handler_interface));
	acceptor.async_accept (connection->socket, boost::asio::bind_executor (connection->strand, [this, connection] (boost::system::error_code const & ec) {
		if (ec != boost::asio::error::operation_aborted && acceptor.is_open ())
		{
			accept ();
		}
		if (!ec)
		{
			connection->parse_connection ();
		}
		else
		{
			logger.always_log (boost::str (boost::format ("Error accepting RPC connections: %1% (%2%)") % ec.message () % ec.value ()));
		}
	}));
}

void nano::rpc::stop ()
{
	stopped = true;
	acceptor.close ();
}

std::unique_ptr<nano::rpc> nano::get_rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a)
{
	std::unique_ptr<rpc> impl;

	if (config_a.tls_config && config_a.tls_config->enable_https)
	{
#ifdef NANO_SECURE_RPC
		impl = std::make_unique<rpc_secure> (io_ctx_a, config_a, rpc_handler_interface_a);
#endif
	}
	else
	{
		impl = std::make_unique<rpc> (io_ctx_a, config_a, rpc_handler_interface_a);
	}

	return impl;
}

Is there a reason to change this to pass by value instead of const ref? I changed it back and everything still compiles and runs.