Смарт-контракты здесь: нам нужно поддержать переход разработчиков в новую парадигму.

Я программист, родился и вырос.

Мой отец посоветовал мне выучить Си, вероятно, до того, как я понял, как правильно писать по-английски. В нашем доме всегда было полно жестких дисков, мониторов и, честно говоря, игр. Именно эта среда в конечном итоге вернула меня к вычислениям после краткого обучения физике в колледже, заставила меня тратить немного больше времени на игры с Биткойном, чем на выполнение наборов задач в 2012 году, и заставила меня взволновать Ethereum, когда он был впервые задуман. Ethereum и Bitcoin были тем, что вдохновило меня в первую очередь на исследования в области безопасности и криптографии.

Вы можете представить себе мое огорчение, когда я понял, что основной язык программирования для смарт-контрактов сводит на нет многие гарантии безопасности блокчейнов в целом.

Перейдем к медным гвоздикам. Похоже, что Solidity вдохновлен Go и Javascript и в конечном итоге является изолированным потомком обоих, с некоторыми из лучших качеств и некоторыми новыми ужасными. Незначительно сложный образец кода Solidity в конечном итоге будет выглядеть примерно так (заимствовано, с небольшими изменениями, из Документации по Solidity):

pragma solidity ^0.4.11;
contract SimpleAuction {
    address public beneficiary;
    uint public auctionStart;
    uint public biddingTime;
    address public highestBidder;
    uint public highestBid;
    mapping(address => uint) pendingReturns;
    bool ended;
    // Events that will be fired on changes.
    event HighestBidIncreased(address bidder, uint amount);
    event AuctionEnded(address winner, uint amount);
    function SimpleAuction(
        uint _biddingTime,
        address _beneficiary
    ) {
        beneficiary = _beneficiary;
        auctionStart = now;
        biddingTime = _biddingTime;
    }
    function bid() payable {
        require(now <= (auctionStart + biddingTime));
        require(msg.value > highestBid);
        if (highestBidder != 0) {
           pendingReturns[highestBidder] += highestBid;
        }
        highestBidder = msg.sender;
        highestBid = msg.value;
        HighestBidIncreased(msg.sender, msg.value);
    }
    function withdraw() returns (bool) {
        uint amount = pendingReturns[msg.sender];
        if (amount > 0) {
            pendingReturns[msg.sender] = 0;
            if (!msg.sender.send(amount)) {
                pendingReturns[msg.sender] = amount;
                return false;
            }
        }
        return true;
    }
    function auctionEnd() {
        require(now >= (auctionStart + biddingTime));
        require(!ended);
        ended = true;
        AuctionEnded(highestBidder, highestBid);
        beneficiary.transfer(highestBid);
    }
}

Одно из самых распространенных утверждений о Solidity состоит в том, что он ощущается похожим на Javascript. Послушайте, если вы один из тех, кто это говорит, я не пытаюсь утверждать, что синтаксис полностью чужой, но сказать, что это похоже на Javascript, - это очень странное заявление. Да, есть ваши обычные function и if и дружественные скобки, и нет, там нет этих забавных := операторов.

Но затем мы переходим к тому факту, что точки с запятой не являются необязательными, типы возврата и объявления являются явными, литерал 0 - это байт (не int), и довольно скоро вы поймете, что почти любой другой язык общего назначения (синтаксис , стиль и использование) для лучшего сравнения. И слава богу за это, учитывая, что статическая типизация очень помогает в расшифровке намерений автора.

Так что же мы на самом деле заимствуем из Javascript? Уж точно не по функциональному ядру. Думаю, мы только что успокоились, узнав, что, говоря о Javascript, мы как-то сделали Solidity более доступным.

И, возможно, в этом и заключается проблема: пытаясь сделать написание смарт-контрактов настолько поверхностно доступным, насколько это возможно, мы невольно поручили критическую задачу разработки единожды развертываемого, с незнакомым шаблоном финансового программного обеспечения разработчикам полного стека, которые привыкли создавать вещи, которые имеют совершенно несравнимые степени неблагоприятных последствий отказа.

Обратите внимание на следующие - хорошо задокументированные - соображения безопасности EVM (воздайте должное команде Solidity, но также обратите внимание на незначительные проблемы):

  • Повторный вход
  • Торможение контракта из-за чрезмерного расхода газа
  • Глубинные атаки callstack

В настоящее время повторное вовлечение - это совершенно чуждое понятие для тех, кто не потратил много времени на изучение и работу с EVM.

И по-прежнему нет никаких инструментов - даже компилятора Solidity! - с предупреждениями об этом.

(править: Кристиан Рейтвизнер поправил меня - Remix IDE предлагает эти предупреждения)

Oyente, по крайней мере, находится в стадии бета-тестирования, а solint, кажется, отошел на второй план, если он вообще когда-либо был. И, конечно же, самая большая проблема заключается в том, что многие из существующих передовых инструментов не считаются обязательными , что является абсурдом. Лучшее, что у нас есть, - это раздел в документации Solidity о шаблоне Проверки-Эффекты-Взаимодействия.

Таким образом, проблема поиска компетентного разработчика, без сомнения, усугубляется тем фактом, что достойных разработчиков на Solidity часто просят подписать соглашения о неразглашении и тому подобное. Из-за этого им немного сложно продемонстрировать соответствующее знание этих странных шаблонов, поскольку они не могут раскрыть какой-либо код, который они на самом деле, вы знаете, закодировали .

Конечно, Эмин Гюн Сирер тоже на это повлиял:

Есть некоторые выводы для разработчиков инструментальных цепочек: компилятор Solidity или инструменты, подобные lint, должны обнаруживать и предупреждать о подобных антишаблонах.

На более высоком уровне я не вижу веских причин, по которым EVM должна включать функцию контракта по умолчанию для участия в произвольно сложном поведении. В частности, EVM может просто запретить контракту B и всем его вызываемым объектам C, D,…, Z выполнять обратные вызовы контракту A, когда B вызывается A, если это явно не разрешено A. То есть, запрет по умолчанию на повторное вхождение между контрактами, если он не отключен. Контракт A по-прежнему может вызывать свои собственные внутренние функции в течение всего дня, но если он вызывает другую функцию, возврата нет.

Solidity также привел к тому, что некоторые очень разработчики попали в тар-ямы, недавно обнаружив ошибку в кошельке, одобренном Parity. Причина? Solidity устанавливает функции как общедоступные по умолчанию, а некоторая логика конструктора для кошелька была извлечена в отдельную библиотеку. Вызов функций автоматически перенаправлялся для любых публичных функций - и конструктор был публичным по умолчанию в Solidity.

И это лишь некоторые из опасений.

В этот момент я уже слышу заточку вил, но, двигаясь вперед, что мы должны сделать, чтобы это исправить?

Для начала - и я собираюсь быть немного знакомым с этим следующим рефреном - нам нужны предупреждения уровня компилятора о повторном входе. Если я работаю в Scala с Intellij IDEA, по умолчанию он предупреждает меня о потенциальных побочных эффектах в монадических преобразованиях, если я добавил их намеренно или иным образом.

Как насчет явных аннотаций для функций, которые зависят от вызовов других контрактов?

Возможно, нам следует рассмотреть возможность внедрения библиотек функционального стиля (а-ля Java 8) для дальнейшего улучшения языка и внушения ценности устранения побочных эффектов?

Давайте не будем перекладывать бремя изучения новых странных шаблонов исключительно на плечи новых разработчиков. Мы должны ожидать большего от наиболее широко используемого в мире языка смарт-контрактов.

Если вам понравилась эта несколько очищающая напыщенная речь, пожалуйста, порекомендуйте и поделитесь ею! Чтобы не отставать от Topl и узнать больше о наших ужасных проблемах и рассказать нам, почему мы не имеем права комментировать Ethereum, вы можете подписаться на нас в Twitter, в нашем блоге, посетить наш сайт, или присоединяйтесь к нашему Slack. Спасибо!