Наша компания пишет много материалов на тему качества кода. Некоторые проекты, выбранные для аудита кода, не очень близки всем читателям, но все из вас точно пользуются финансовыми приложениями. Может, не конкретно этим, но статья о том, что разработка программ в этой сфере без применения подходов контроля качества несёт потенциальные риски для всех пользователей. BTCPay Server — это бесплатный биткойн-платежный процессор с открытым исходным кодом, который позволяет принимать биткойны без комиссий и посредников. Приложение активно разрабатывается и доступно на GitHub. Проект было очень легко проверить с помощью статического анализатора PVS-Studio, т. к. он написан на C# и открывается в Visual Studio. Для этой IDE у нас есть плагин, позволяющий запускать анализ и визуализировать результаты. В статью вошли 10 самых интересных примеров кода, которые анализатор посчитал ошибочными. V3022 Expression 'request.PaymentTolerance < 0 && request.PaymentTolerance &gt; 100' is always false. Probably the '||' operator should be used here. BTCPayServer\Controllers\GreenField\GreenfieldStoresController.cs 241 С отменой операций из-за неверного ввода в финансовых приложениях сталкивался каждый. Иногда приложения не очень удачно уведомляют пользователя о неверных данных, но иногда в коде просто есть глупая ошибка. Так, в этом фрагменте кода перепутали операторы '&&' и '||'. Код предназначался для проверки значения в диапазоне от 0 до 100, но в итоге предупреждение пользователю не будет выдано. Как результат, неверно введённое значение пройдёт дальше по коду и может привести к ошибке в логике приложения. V3001 There are identical sub-expressions 'e.Name == InvoiceEvent.FailedToConfirm' to the left and to the right of the '||' operator. BTCPayServer\HostedServices\BitpayIPNSender.cs 264 Одинаковые сравнения свидетельствуют о том, что этот фрагмент кода писался методом copy-paste. Как результат, в сравнении участвуют две одинаковые константы.  Существует две трактовки найденного предупреждения: Одна из констант лишняя, надо удалить. После копирования забыли переименовать константу. Я полагаю, что здесь именно второй случай, который является ошибкой. Давайте взглянем на все константы: Все константы можно условно разделить на 3 группы: Создание платежей. Подтверждение платежей. Завершение платежей. Тогда методом исключения можно предположить, что одна из копий должна быть константой PaidAfterExpiration, т. к. дублирующийся код и неиспользованная константа похожи на записи из группы про завершение платежей. V3061 Parameter 'storeId' is always rewritten in method body before being used. BTCPayServer\Controllers\UIStoresController.cs 890 Значение параметра storeId перезаписывается до его использования. В большинстве случаев это свидетельствует об ошибке, т. к. код функции уже не соответствует её интерфейсу. V3095 The 'request' object was used before it was verified against null. Check lines: 355, 364. BTCPayServer\Controllers\GreenField\GreenfieldPullPaymentController.cs 355 Обращение к переменной request сначала происходит без проверок, а потом с ними. С этим кодом точно что-то не так. V3008 The 'model.StoreName' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 166, 165. BTCPayServer\Controllers\GreenField\GreenfieldStoresController.cs 166 Анализатор обнаружил два одинаковых присваивания. Как и в примере номер 2, тут очевидный copy-paste стиль написания кода. Возможно, здесь присутствует ошибка. V3029 The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 59, 64. BTCPayServer\Hosting\BTCpayMiddleware.cs 59 Переменная isBitpayAPI проверяется два раза подряд. При этом она не изменяется, а блоки условных выражений сильно отличаются. Можно предположить, что в блоке первого условия надо обновить состояние этой переменной таким образом: V3125 The 'request' object was used after it was verified against null. Check lines: 136, 130. BTCPayServer\Controllers\GreenField\GreenfieldLightningNodeApiController.cs 136 Этот фрагмент кода похож на пример 4: тоже переменная request то проверяется на валидность, то нет. Но тут ошибка имеет другой характер. В проекте присутствуют похожие участки кода, но содержащие дополнительный код между приведёнными условиями: Т. е. во многих местах присутствует косвенная проверка, а в этом месте забыли её добавить. V3168 Awaiting on expression with potential null value can lead to NullReferenceException. BTCPayServer\HostedServices\InvoiceWatcher.cs 383 Мы видим оператор '?.', наличие которого предполагает возращение значения null. В этом случае await null приведёт к исключению NullReferenceException.  V3022 Expression 'items == null' is always false. BTCPayServer\Services\Invoices\InvoiceRepository.cs 427 Метод расширения Where возвращает не null, а пустую коллекцию, если в ней не оказалось подходящих условию элементов. Хотя в данном случае метод самописный, его реализация всё равно обращается к стандартному методу: Можно предложить, что автор кода хотел не звать метод сохранения изменений для пустой коллекции, но из-за неправильного сравнения это всё равно происходит. V3108 It is not recommended to return 'null' from 'ToString()' method. BTCPayServer\Controllers\UILNURLController.cs 364 Может не ошибка, но возвращать значение null из перегруженного метода ToString не очень хорошая идея, в том числе по рекомендациям Microsoft. Он вызывается в очень многих ситуациях, иногда даже неочевидным для разработчика образом, что может привести к потенциальным исключениям в приложении. Всего анализатор выдал 239 предупреждений, что вполне нормально для такого небольшого проекта. Там ещё есть что поисследовать. Но 10 предупреждений, как в примерах, могут снизить доверие к приложению, работающему с финансовой информацией. Можно предположить, что методология статического анализа в проект никогда не внедрялась, даже с использованием бесплатных инструментов. Будем рады, если разработчики рассмотрят наше решение первым для контроля качества кода. Готовы предоставить временную лицензию. Чтобы не повторить ошибок авторов проекта BTCPay Server, скачайте PVS-Studio. Он поддерживает анализ таких языков, как C, C++, C# и Java. Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Sviatoslav Razmyslov. BTCPay Server: top 10 bugs in Bitcoin payment processor code.  Product Manager