Смарт-контракты здесь: нам нужно поддержать переход разработчиков в новую парадигму.
Я программист, родился и вырос.
Мой отец посоветовал мне выучить Си, вероятно, до того, как я понял, как правильно писать по-английски. В нашем доме всегда было полно жестких дисков, мониторов и, честно говоря, игр. Именно эта среда в конечном итоге вернула меня к вычислениям после краткого обучения физике в колледже, заставила меня тратить немного больше времени на игры с Биткойном, чем на выполнение наборов задач в 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. Спасибо!