Update: 13.11.2018 вышел релиз, вот подробное описание изменений. В статье рассказывается про состояние этой версии в мае (за пять месяцев до релиза).
Хочу рассказать об изменениях языка Solidity, которые ожидаются в версии 0.5.0. Сразу отмечу, что я ограничусь только языком — его грамматикой и семантикой.
Какого-то вменяемого текста на эту тему нет даже на английском языке, но недавно в репозитории Solidity появился проект.
По нему можно отслеживать прогресс подготовки версии 0.5.0.
Disclaimer: в статье описано текущее состояние проекта, к релизу многое может поменяться. Точную информацию можно получить из официального changelog'a.
Окончательный запрет устаревших конструкций
В Solidity накопилось довольно много устаревших конструкций, которые хотелось бы удалить из языка, но все никак не удается из-за обратной совместимости. В эту категорию изменений попадают:
- Отказ от
throwв пользуrevert()/assert(...)/require(...). Механизм откатывания транзакции отличается от механизма исключений, и хочется подчеркнуть эту разницу на уровне языка. - Отказ от
var, который с учетом правил вывода типов легко приводит к ошибкам, например:
for(var i = 0; i < 100500; i++) { ... // бесконечный цикл
Дополнительно обсуждают, на что заменить конструкции вроде
var (z, y, z) = foo();
и как поэлегантнее пропускать ненужные значения. - Запретили использовать
constantдля функций — всюду должно быть view или pure. - Встроенная
функция gasleft()вместоmsg.gas. Так понятнее, что это не какая-то константа, а оставшееся количество газа. Да,gasleft()можно переопределить. - Перенесли
block.blockhashвblockhash. Логично, ведьblockhashтекущего блока недоступен (block.blockhash(block.number) == 0). - Запретили смешивать шестнадцатеричные константы и множители времени/эфира. В самом деле, не совсем понятно, что такое
0xaf days. - Отказались от
suicide/sha3в inline assembly. - Отказ от унарного плюса, потому что он не имеет какой-то специальной роли и может участвовать в глупых ошибках (вроде
x=+1;вместоx+=1;). - Планируется отказ от множителя
years, потому что сейчас он определен как365 days, и это не слишком точно соотносится с привычным календарем. Мне это представляется спорным решением — казалось бы, пока можно ограничиться предупреждением.
Более строгий синтаксис
В Solidity очень разные по смыслу конструкции имеют схожий синтаксис. Это мешает читать код, но еще больше это портит жизнь разработчикам инструментов для работы с исходниками (это касается компилятора в том числе). Приятно видеть, что работы ведутся и в этом направлении тоже.
- Обязательное ключевое слово
emitпри генерации событий.
Создание эвента синтаксически не отличалось от вызова функции (и создания структуры). Особенно ярко эта проблема проявлялась в стандарте ERC20, в котором есть функция и эвент с одинаковой “сигнатурой”. - Новый синтаксис для конструкторов:
constructor(<args>) public { ....
С выходом 0.5.0 это будет единственный вариант — функции, чьи имена совпадают с контрактом, запрещены.
Это изменение решает проблему переименования файлов, когда конструктор внезапно превращается в обычную функцию и его можно вызывать повторно или не вызывать вовсе.
Более обоснованным это решение выглядит, если вспомнить, что констрктор может быть толькоpublicилиinternal, не может бытьviewилиpure, не может иметь возвращаемых значений, т.е. является особой сущностью.
Любопытно, что при этом было открыто issue с предложением разрешить одноименные контракту функции в 0.6.0. - Изменение правил видимости локальных переменных — с принятых в Javascript на C99/C++.
Мое любимое. Ничего не могу с собой сделать, каждый раз радуюсь, когда показываю, как компилируется код
x = 1; revert(); uint x; }``` - Принудительное включение строгого режима для ассемблера. Он и сейчас доступен с опцией компилятора
--strict-assembly. В нем ограничены манипуляции со стеком и недоступны метки и переходы — вместо них предлагается использовать более привычные управляющие конструкции вродеforилиswitch. - Запретили использовать адреса без чек-суммы или неправильной длины (отличной от 20 байт). Неплохая идея, хотя с непривычки и вызовет трудности — придется срочно учиться писать адреса с чек-суммой, а вместо привычного
0x0использоватьaddress(0). - Запретили объявлять пустые структуры (
struct A {}). Не очень-то и хотелось, но раньше грамматика такое позволяла.
Модификаторы видимости и ABI
С модификаторами видимости был легкий бардак, во многом обусловленный наличием значений по умолчанию. Накопилось достаточно много мелких правок, которые должны сделать язык более строгим, убрав эту путаницу.
А поскольку эти модификаторы попадают в ABI контракта, изменения коснулись и его.
- Все функции в интерфейсах надо явно помечать как external.
К сожалению, переопределить потом такую функцию как public в некоторых версиях компилятора нельзя — возникнет ошибка (например, в 0.4.21). Это стало возможным начиная с версии 0.4.22.
Также обещают разрешить реализовыватьexternal viewфункции с помощьюpublicпеременных. - Модификаторы видимости стали обязательными для функций.
Это изменение долго ждало своего часа. Issue на гитхабе создали сразу после первого хака Parity.
Дополнительное ограничение — fallback может быть только external.
Функции с произвольным количеством аргументов и упаковка данных
В Solidity есть несколько функций, которые принимают любое количество аргументов, склеивают их одну бинарную колбасу и дальше работают уже с ней. В этом процессе много тонкостей и неочевидных моментов, и сейчас решили с ними разобраться.
- Новый глобальный объект
abiи его методы
encode,encodePacked,encodeWithSelectorиencodeWithSignature, позволяющие контролировать сборку данных для вызова или хэширования.
Предлагается с их помощью собирать аргументы дляkeccak256/sha256/ripemd160иcall/delegatecall. Планируется поменять синтаксис этих команд так, чтобы они не могли принимать список аргументов произвольной длины. - Изменение в автовыведении типов для констант в конструкциях с плотной упаковкой аргументов: в конструкциях вроде
keccak256(1)теперь используется не наименьший достаточный тип (uint8), аuint256. Если такое поведение не устраивает, то придется использовать явное приведение типов (keccak256(uint8(1))).
Это изменение выглядит логично рядом с отказом отvarи высокой (хотя и конечной) точностью вычислений константных выражений. - Изменение правил упаковки массивов. Началось все с проблемы мультисиг кошельков, которые не могли выполнять транзакции контрактов, пытавшихся защититься от short address attack. Видимо, текущее поведение посчитали достаточно неочевидным, а может, это подготовка к введению проверки длины
msg.dataна уровне EVM.
Улучшение "работы с памятью"
В кавычках, потому что речь в основном идет о "неожиданном" доступе к storage без использования assembly.
- Запрет непроинициализированных ссылок на storage, которые на деле указывали в начало storage, и поэтому пересекались с другими переменными состояния.
- Может быть запретят прямую работу с
<array>.length.
Изменение длины массива вручную — достаточно низкоуровневая операция. Ее основной плюс — экономия газа при небольших изменениях длины массива. Но такой синтаксис позволяет случайно (или умышленно) создать массив неадекватного размера, что может привести к overlap attack. Для очистки массива давно естьdelete, для уменьшения размера предлагается использоватьpop(), помимо этого обсуждают операции вродеtruncate()иextend(). Ну и по-прежнему есть assembly, если очень надо.
Из сомнительного
В любой бочке меда окажется своя ложка дегтя.
- Добавляют целую вязанку ключевых слов на все случаи жизни. Часть из них выглядит угрожающе, но пока ничего не понятно. Честно говоря, надеялся увидеть в этом списке
revert,assertиrequire, но с точки зрения грамматики они остаются просто функциями (и их можно переопределить).
Не про язык
Есть несколько очень важных изменений, которые не касаются непосредственно языка, но при этом сильно повлияют на код новых контрактов.
- Наконец-то появятся числа с дробной частью. Вроде все привыкли обходиться без них, а теперь будем учиться использовать их правильно.
- На уровне EVM добавится защита от short address attack. Казалось бы, мелочь, но приятно, что об этом не надо больше думать (и вообще знать об этой проблеме). Может быть, она будет даже более строгой, но там есть свои трудности.
Какие-то выводы
Хотя дата выхода релиза еще неизвестна, многие новшества уже можно потрогать руками.
Включаются они с помощью
pragma experimental "v0.5.0";
и
pragma experimental "ABIEncoderV2";
Компилятор, конечно, выдает предупреждение.
В целом, 0.5.0 воспринимается позитивно. Удалят то, от чего никак не удается избавиться из-за обратной совместимости, плотно отрефакторят пару скользких тем, внесут несколько полезных изменений. Потом будем ждать рефакторинг наследования, а там, может, и Vyper подоспеет.