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

Теория разума

Существует теория разума, основанная на языке с названием, которое звучит так, будто оно пришло прямо из «Звездного пути».

Гипотеза Сепира-Уорфа.

Проще говоря, предполагается, что грамматика и словарный запас языка, на котором мы думаем, и общество, в котором мы живем и которое породило этот язык, глубоко влияют на то, как мы интерпретируем мир вокруг нас. Вместе они создают и ограничивают то, о чем мы можем мысленно догадываться.

«Мы разрезаем природу, организуем ее в понятия и приписываем значения, как мы это делаем, в основном потому, что мы являемся сторонами соглашения об организации ее таким образом — соглашения, которое действует во всем нашем речевом сообществе и кодифицировано в образцах нашего языка».
— Бенджамин Ли Уорф [1]

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

Если бы лев мог говорить, мы бы его не поняли
— Людвиг Витгенштейн [2]

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

функция мысли(язык) { …

Любому любознательному разработчику программного обеспечения, читающему о таких вещах, на ум приходит вопрос: «Существенно ли язык программирования, который я выбираю для программирования, влияет на то, как я думаю о текущих задачах? Я ограничиваю себя тем, что пишу только на C++?»

(Ответ на оба эти вопроса: «Возможно, да».)

Вы не будете первыми, у кого возникнут такие мысли. Вот что сказал об этом Пол Грэм пару десятков лет назад:

«[Программисты] довольны любым языком, который они используют, потому что он диктует, как они думают о программах»
— Пол Грэм [3]

Но это касается не только программистов.

Если вы инженер-испытатель или аудитор, и вы просматриваете смарт-контракты, проблема диссонанса языков программирования может стоять особенно остро. Вы можете использовать один язык, скажем, JavaScript, для написания тестов для смарт-контрактов, написанных на другом языке, например, Solidity.

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

Повелительное наклонение

JavaScript и Solidity синтаксически не так уж отличаются. Оба они являются императивными языками — абстракциями языка ассемблера. И их синтаксис очень похож; это как сравнивать испанский с итальянским. Но есть некоторые заметные проблемы. Например, при передаче значения из JavaScript в смарт-контракт Solidity с использованием Hardhat вы часто будете полагаться на то, что ваша структура неявно преобразует строку JavaScript в байты Solidity. Закулисное преобразование может привести к тому, что ошибки проскользнут сквозь трещины вашего тестового покрытия.

Несоответствие становится еще больше, когда вы работаете с двумя императивными языками программирования с разными фундаментальными смыслами существования.

Например, Rust, который скомпилирован и требует понимания базовой механики выделения и освобождения памяти, по сравнению с Python, который больше похож на интерпретируемый язык «быстрого выполнения задач» с автоматической сборкой мусора. В данном случае мы рассматриваем программный эквивалент сравнения немецкого языка с французским.

По крайней мере, они оба индоевропейские языки.

Большинство программистов, с которыми я работал, работали с кодом преимущественно в императивном стиле, а поток управления составлял основу их кода.

Но есть еще один важный класс языков программирования: чисто функциональные, такие как Lisp и Haskell. Если вы используете JavaScript для тестирования смарт-контракта Cardano на языке Plutus, похожем на Haskell, то вы выполняете кодирование, эквивалентное переключению между немецким (индоевропейский язык) и финским (финно-угорский язык с совершенно другим набором грамматических правил, даже если он использует тот же алфавит).

Фреймворки

Михал использует Foundry для написания и тестирования смарт-контрактов Ethereum, в то время как я придерживаюсь набора инструментов Hardhat для этих действий. Это означает, что он пишет свои тесты на Solidity, а я пишу на JavaScript. И поскольку мы оба тестируем контракты Solidity, ему не нужно переключать контекст, а мне нужно.

Если у вас есть время и бюджет, в идеале вы бы наняли версию Михала и одного из меня для проверки вашего контракта — дублирования будет много, но совпадение наших тестов не будет на 100%.

Частично это связано с тем, что мы программируем и думаем на разных языках.

Но есть еще один аспект, который стоит учитывать.

Общество как основа

Проблема с Solidity заключается в том, что все действительно серьезные ошибки связаны с окружением, а не с кодом. Это означает, что они зависят как от EVM — от того, как данные хранятся и выполняются в Ethereum, так и от строк кода.

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

Неудачный вывод заключается в том, что для действительно тщательного тестирования смарт-контракта вам необходимо свободно владеть не только языком контракта, но и средой или «культурой», которая существует вокруг этого контракта. И это будет включать в себя структуру тестирования.

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

В блокчейне это действительно сложно. Очень мало людей, которые являются коренными в одной цепочке и свободно владеют другой. Откровенно говоря, очень мало людей, свободно владеющих какой-либо цепочкой, не говоря уже о родной.

На самом деле мосты должны быть написаны, а затем протестированы людьми, полностью знакомыми с обеими цепями.

Заключение

В рамках регулярного общения с инженерами Resonance Security я обмениваюсь идеями с людьми из разных слоев общества, которые думают на разных языках и пишут код на разных языках.

На этой неделе я разговаривал с Михалом, поляком, который в основном использует Rust. На прошлой неделе я разговаривал с Жоао, португальцем, предпочитающим Python. А на следующей неделе я собираюсь взять интервью у Луиса, который сообщил мне, что думает по-испански. Со временем я узнаю, какой у него любимый язык программирования.

Это много языков и, следовательно, много разных точек зрения.

Учитывая достаточное количество глазных яблок, все ошибки поверхностны[4], и то же самое вполне может относиться к уязвимостям программного обеспечения и аудиту.

Но это не работает, если все глазные яблоки одинаковы...

Рекомендации

[1] Б. Л. Уорф (1956): Язык, мысль и реальность (изд. Дж. Б. Кэрролл). Кембридж, Массачусетс: MIT Press

[2] Л. Витгенштейн (1968), Philosophical Investigations (пер. GEM Anscombe). Нью-Йорк: Макмиллан

[3] П. Грэм (2001 г.), Выше среднего. Получено с https://www.cs.tufts.edu/comp/150FP/archive/paul-graham/sec.pdf

[4] Э. С. Раймонд (2000), Закон Линуса, получено с http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html